65. Valid Number
- String
- Simulation
TIME
O(n)
SPACE
O(1)
Intuition
The grammar looks fiddly, but every rule is local, whether a character is legal depends only on what has been seen so far, never on what comes later. That means one left-to-right scan carrying three booleans is enough, with no backtracking and no regex.
The three pieces of state:
seenDigithas a digit appeared in the current mantissa/exponent section?seenDothas a decimal point appeared?seenExphase/Eappeared?
Two rules carry most of the weight:
- A dot is illegal once an exponent has started. The exponent must be an integer, which is what rejects
"99e2.5". eresetsseenDigit. The exponent needs digits of its own, so after consumingethe flag goes back to false, that single reset is what rejects"1e"while still accepting"2e10".
A sign is legal only in two spots: the very start, or directly after e/E. That covers "3e+7" while rejecting "--6" and "-+3".
Approach
Scan each character:
- digit → set
seenDigit. +or-→ valid only at index0or when the previous character ise/E; otherwise return false..→ return false ifseenDotorseenExp; otherwise setseenDot.eorE→ return false ifseenExpor notseenDigit; otherwise setseenExpand clearseenDigit.- anything else → return false.
Finally return seenDigit, which guarantees the string ended with at least one digit in its last section.
Complexity
- Time: O(n), one pass
- Space: O(1), three booleans
Edge Cases
"."→ a dot with no digits →seenDigitis false at the end → invalid"e"/"e3"→ exponent with no preceding digit → invalid"1e"→ exponent with no following digit; caught by theseenDigitreset → invalid"4."and"-.9"→ both valid; a dot may lead or trail as long as some digit exists"99e2.5"→ dot after exponent → invalid"--6","-+3"→ sign not at the start or aftere→ invalid- Uppercase
Ebehaves identically toe("-90E3")
Code
class Solution:
def isNumber(self, s: str) -> bool:
"""
65. Valid Number
Time: O(n)
Space: O(1)
"""
seen_digit = False
seen_dot = False
seen_exp = False
for i, ch in enumerate(s):
if ch.isdigit():
seen_digit = True
elif ch in "+-":
# a sign is only legal at the start or right after e/E
if i > 0 and s[i - 1] not in "eE":
return False
elif ch == ".":
# the exponent must be an integer, so no dot after e/E
if seen_dot or seen_exp:
return False
seen_dot = True
elif ch in "eE":
if seen_exp or not seen_digit:
return False
seen_exp = True
seen_digit = False # the exponent needs digits of its own
else:
return False
return seen_digit/**
* 65. Valid Number
* Time: O(n)
* Space: O(1)
*/
class Solution {
public boolean isNumber(String s) {
boolean seenDigit = false;
boolean seenDot = false;
boolean seenExp = false;
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (Character.isDigit(ch)) {
seenDigit = true;
} else if (ch == '+' || ch == '-') {
// a sign is only legal at the start or right after e/E
if (i > 0) {
char prev = s.charAt(i - 1);
if (prev != 'e' && prev != 'E') {
return false;
}
}
} else if (ch == '.') {
// the exponent must be an integer, so no dot after e/E
if (seenDot || seenExp) {
return false;
}
seenDot = true;
} else if (ch == 'e' || ch == 'E') {
if (seenExp || !seenDigit) {
return false;
}
seenExp = true;
seenDigit = false; // the exponent needs digits of its own
} else {
return false;
}
}
return seenDigit;
}
}