Introduction
LeetCode 65: Valid Number is a classic string parsing problem where you're asked to determine if a given string represents a valid number. The challenge lies in the variety of acceptable formats—integers, floating-point numbers, scientific notation (like 2e10
)—and handling optional signs and spaces.
This problem is notoriously tricky because of all the edge cases, and while many use regular expressions, a cleaner and more maintainable approach is to implement a state machine manually or use flag-based parsing.
Problem Statement
Validate if a given string can be interpreted as a decimal number.
Examples:
InputOutput"0"true"e"false".1"true"1e10"true"1e"false" -90e3 "true" 1e"false" 1e.1 "false"6e6.5"false"99e2.5"false"--6"false"-+3"false
Constraints
- The input can be a number with or without a decimal.
- It can include exponential notation (
e
or E
) with a valid integer after.
- Leading/trailing whitespaces should be ignored.
- Signs (
+
, -
) may appear at the beginning or after e
.
- Input will be a string of any printable ASCII character.
Approach: Flag-Based Parser
We’ll scan through the string and maintain several flags:
numSeen
: At least one digit seen.
dotSeen
: Decimal point has appeared.
eSeen
: Exponent character (e
or E
) has appeared.
numAfterE
: Digit appears after exponent.
This avoids using regex and is more robust and performant.
Go Implementation
go
import "strings"
func isNumber(s string) bool {
s = strings.TrimSpace(s)
if len(s) == 0 {
return false
}
numSeen := false
dotSeen := false
eSeen := false
numAfterE := true
for i := 0; i < len(s); i++ {
ch := s[i]
switch {
case ch >= '0' && ch <= '9':
numSeen = true
if eSeen {
numAfterE = true
}
case ch == '.':
if dotSeen || eSeen {
return false
}
dotSeen = true
case ch == 'e' || ch == 'E':
if eSeen || !numSeen {
return false
}
eSeen = true
numAfterE = false // must be a number after e
case ch == '+' || ch == '-':
if i != 0 && s[i-1] != 'e' && s[i-1] != 'E' {
return false
}
default:
return false
}
}
return numSeen && numAfterE
}
Explanation
- Trim Whitespace: Remove leading and trailing spaces.
- Traverse the string:
- If digit: set
numSeen = true
.
- If
.
:
- Must not have seen a dot or
e
.
- If
e
or E
:
- Must not have seen an exponent before.
- Must have at least one digit before
e
.
- Reset
numAfterE
to ensure digits follow the exponent.
- If sign:
- Must be at the beginning or immediately after an exponent.
- Any other character: return
false
.
- Return: Must have seen a valid number and a valid digit after
e
if present.
Time and Space Complexity
MetricComplexityTime ComplexityO(n)Space ComplexityO(1)
- We traverse the string once (
O(n)
).
- No extra data structures are used (
O(1)
).
Edge Cases Covered
- Empty string:
false
- Only a dot or just
e
: false
- Numbers with trailing spaces:
true
- Exponents without digits after:
false
- Valid float with no integer part:
.5
→ true
Conclusion
LeetCode 65 tests not just your ability to parse strings but your attention to detail in handling edge cases. While regex is tempting, a flag-based parser is often more readable and controllable for interviews and production systems alike.