Skip to content

Commit 6a995cb

Browse files
reniz-shahpre-commit-ci[bot]cclauss
authored
add algorithm to check if given string is valid number or not (#11479)
* add algorithm to check if given string is valid number or not Add a new file string_is_valid_number.py in string directory * resolve comments which is create enum for states and char types extract state machine outside function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * resolve comments which is create enum for states and char types extract state machine outside function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * resolve ruff check failing * change variable name from s to number_string as it is more descriptive * rebase branch with master * resolve comments which is create enum for states and char types extract state machine outside function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * resolve ruff check failing * resolve ruff check failing * updating DIRECTORY.md * Fix link formatting in docstring --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss <cclauss@me.com> Co-authored-by: cclauss <cclauss@users.noreply.github.com>
1 parent 836bd71 commit 6a995cb

2 files changed

Lines changed: 170 additions & 0 deletions

File tree

‎DIRECTORY.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,7 @@
16081608
* [Reverse Words](strings/reverse_words.py)
16091609
* [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py)
16101610
* [Split](strings/split.py)
1611+
* [String Is Valid Number](strings/string_is_valid_number.py)
16111612
* [String Switch Case](strings/string_switch_case.py)
16121613
* [Strip](strings/strip.py)
16131614
* [Suffix Automaton](strings/suffix_automaton.py)

‎strings/string_is_valid_number.py‎

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"""
2+
Topic: Deterministic Finite Automaton (DFA)
3+
Given a string s, return whether s is a valid number or not
4+
LeetCode link: https://leetcode.com/problems/valid-number/description/
5+
"""
6+
7+
from enum import Enum
8+
9+
10+
class CharType(Enum):
11+
NUMERIC = "NUMERIC"
12+
SIGN = "SIGN"
13+
EXPONENT = "EXPONENT"
14+
DECIMAL = "DECIMAL"
15+
16+
17+
class State(Enum):
18+
INITIAL = "INITIAL"
19+
SIGNED = "SIGNED"
20+
WHOLE = "WHOLE"
21+
FRACTIONAL = "FRACTIONAL"
22+
FRACTION = "FRACTION"
23+
EXPONENTIAL = "EXPONENTIAL"
24+
EXP_SIGN = "EXP_SIGN"
25+
EXP_NUMBER = "EXP_NUMBER"
26+
27+
28+
state_machine: dict[State, dict[CharType, State]] = {
29+
State.INITIAL: {
30+
CharType.NUMERIC: State.WHOLE,
31+
CharType.SIGN: State.SIGNED,
32+
CharType.DECIMAL: State.FRACTIONAL,
33+
},
34+
State.SIGNED: {CharType.NUMERIC: State.WHOLE, CharType.DECIMAL: State.FRACTIONAL},
35+
State.WHOLE: {
36+
CharType.NUMERIC: State.WHOLE,
37+
CharType.DECIMAL: State.FRACTION,
38+
CharType.EXPONENT: State.EXPONENTIAL,
39+
},
40+
State.FRACTIONAL: {CharType.NUMERIC: State.FRACTION},
41+
State.FRACTION: {
42+
CharType.NUMERIC: State.FRACTION,
43+
CharType.EXPONENT: State.EXPONENTIAL,
44+
},
45+
State.EXPONENTIAL: {
46+
CharType.NUMERIC: State.EXP_NUMBER,
47+
CharType.SIGN: State.EXP_SIGN,
48+
},
49+
State.EXP_SIGN: {CharType.NUMERIC: State.EXP_NUMBER},
50+
State.EXP_NUMBER: {CharType.NUMERIC: State.EXP_NUMBER},
51+
}
52+
53+
54+
def classify_char(char: str) -> CharType | None:
55+
"""
56+
Classifies a character into one of the following categories:
57+
58+
- 'CharType.NUMERIC': if the character is a digit (0-9)
59+
- 'CharType.SIGN': if the character is a plus sign (+) or a minus sign (-)
60+
- 'CharType.EXPONENT': if the character is an 'e' or 'E'
61+
(used in exponential notation)
62+
- 'CharType.DECIMAL': if the character is a decimal point (.)
63+
- None: if the character does not fit into any of the above categories
64+
- None: if size of char is not 1
65+
66+
Parameters:
67+
char (str): The character to be classified
68+
69+
Returns:
70+
CharType: The classification of the character
71+
72+
>>> classify_char('2')
73+
<CharType.NUMERIC: 'NUMERIC'>
74+
>>> classify_char('-')
75+
<CharType.SIGN: 'SIGN'>
76+
>>> classify_char('e')
77+
<CharType.EXPONENT: 'EXPONENT'>
78+
>>> classify_char('.')
79+
<CharType.DECIMAL: 'DECIMAL'>
80+
>>> classify_char('')
81+
82+
>>> classify_char('0')
83+
<CharType.NUMERIC: 'NUMERIC'>
84+
>>> classify_char('01')
85+
"""
86+
if len(char) != 1:
87+
return None
88+
if char.isdigit():
89+
return CharType.NUMERIC
90+
if char in "+-":
91+
return CharType.SIGN
92+
if char in "eE":
93+
return CharType.EXPONENT
94+
if char == ".":
95+
return CharType.DECIMAL
96+
return None
97+
98+
99+
def is_valid_number(number_string: str) -> bool:
100+
"""
101+
This function checks if the input string represents a valid number.
102+
It uses a finite state machine to parse the input string,
103+
transitioning between states based on the character type.
104+
The function returns True if the input string represents a valid number,
105+
and False otherwise.
106+
A valid number is defined as a string that can be parsed into an
107+
integer, decimal, or exponent.
108+
>>> is_valid_number("2")
109+
True
110+
>>> is_valid_number("0089")
111+
True
112+
>>> is_valid_number("-0.1")
113+
True
114+
>>> is_valid_number("+3.14")
115+
True
116+
>>> is_valid_number("4.")
117+
True
118+
>>> is_valid_number("-.9")
119+
True
120+
>>> is_valid_number("2e10")
121+
True
122+
>>> is_valid_number("-90E3")
123+
True
124+
>>> is_valid_number("3e+7")
125+
True
126+
>>> is_valid_number("+6e-1")
127+
True
128+
>>> is_valid_number("53.5e93")
129+
True
130+
>>> is_valid_number("-123.456e789")
131+
True
132+
133+
134+
>>> is_valid_number("abc")
135+
False
136+
>>> is_valid_number("1a")
137+
False
138+
>>> is_valid_number("1e")
139+
False
140+
>>> is_valid_number("e3")
141+
False
142+
>>> is_valid_number("99e2.5")
143+
False
144+
>>> is_valid_number("--6")
145+
False
146+
>>> is_valid_number("-+3")
147+
False
148+
>>> is_valid_number("95a54e53")
149+
False
150+
>>> is_valid_number(".")
151+
False
152+
"""
153+
154+
valid_final_states = {State.WHOLE, State.FRACTION, State.EXP_NUMBER}
155+
current_state = State.INITIAL
156+
157+
for char in number_string:
158+
char_type = classify_char(char)
159+
if char_type is None or char_type not in state_machine[current_state]:
160+
return False
161+
current_state = state_machine[current_state][char_type]
162+
163+
return current_state in valid_final_states
164+
165+
166+
if __name__ == "__main__":
167+
import doctest
168+
169+
doctest.testmod()

0 commit comments

Comments
 (0)