LC 0065 [H] Valid Number - ALawliet/algorithms GitHub Wiki
this problem just sucks with too many edge cases, need to just memorize
"459277e38+" false
"3." true
"." false
"0e" false
We use three flags: met_dot, met_e, met_digit, mark if we have met ., e or any digit so far. First we strip the string, then go through each char and make sure:
- If char is + or -, then prev char (if there is one) must be e
- . cannot appear twice or after e
- e cannot appear twice, and there must be at least one digit before and after e
- All other non-digit char is invalid
'e' or 'E'
class Solution:
def isNumber(self, s: str) -> bool:
dot = e = digit = False # store the last previously met
for i, x in enumerate(s):
# check the for 'e' types
if x == 'E': x = x.lower()
if x in '+-':
if i > 0 and s[i-1] != 'e': return False # sign must come right after e, must be like 'e+7', 'e-1', so need to check [i-1]
elif x == '.' : # not dot and not e
if dot or e: return False # digit must come before ., can't be consec '..' or after 'e.'
dot = True
# e = False # not needed, but makes sense
elif x == 'e': # not e and digit
if e or not digit: return False # can't be like consec 'ee', must be like '1e'
e = True # set next e
digit = False # consume prev digit like '0e'
# if we get past here it must either be a digit or not
elif x.isdigit():
digit = True
else:
return False
return digit
class Solution(object):
def isNumber(self, s):
"""
:type s: str
:rtype: bool
"""
s = s.strip()
met_dot = met_e = met_digit = False
for i, char in enumerate(s):
# If char is + or -, then prev char (if there is one) must be e
if char in ['+', '-']:
if i > 0 and s[i-1].lower() != 'e':
return False
# . cannot appear twice or after e
elif char == '.':
if met_dot or met_e:
return False
met_dot = True
# e cannot appear twice, and there must be at least one digit before and after e
elif char.lower() == 'e':
if met_e or not met_digit:
return False
met_e = True
met_digit = False
elif char.isdigit():
met_digit = True
# All other non-digit char is invalid
elif not char.isdigit():
return False
# digit must come after e, can't end in e
return met_digit
DFA = deterministic finite automaton
class Solution(object):
def isNumber(self, s):
"""
:type s: str
:rtype: bool
"""
#define a DFA
state = [{}, # 0
{'blank': 1, 'sign': 2, 'digit':3, '.':4}, #q1
{'digit':3, '.':4}, #q2
{'digit':3, '.':5, 'e':6, 'blank':9}, #q3
{'digit':5}, #q4
{'digit':5, 'e':6, 'blank':9}, #q5
{'sign':7, 'digit':8}, #q6
{'digit':8}, #q7
{'digit':8, 'blank':9}, #q8
{'blank':9}] #q9
q = 1 # current state
for c in s:
if '0' <= c <= '9':
c = 'digit'
elif c == ' ':
c = 'blank'
elif c in ['+','-']:
c = 'sign'
if c not in state[q].keys(): # no next state
return False
q = state[q][c]
if q not in [3,5,8,9]: # ending states with 2 circles
return False
return True
#define DFA state transition tables
states = [{},
# State (1) - initial state (scan ahead thru blanks)
{'blank': 1, 'sign': 2, 'digit':3, '.':4},
# State (2) - found sign (expect digit/dot)
{'digit':3, '.':4},
# State (3) - digit consumer (loop until non-digit)
{'digit':3, '.':5, 'e':6, 'blank':9},
# State (4) - found dot (only a digit is valid)
{'digit':5},
# State (5) - after dot (expect digits, e, or end of valid input)
{'digit':5, 'e':6, 'blank':9},
# State (6) - found 'e' (only a sign or digit valid)
{'sign':7, 'digit':8},
# State (7) - sign after 'e' (only digit)
{'digit':8},
# State (8) - digit after 'e' (expect digits or end of valid input)
{'digit':8, 'blank':9},
# State (9) - Terminal state (fail if non-blank found)
{'blank':9}]