Valid Number
Solution 1
整理一下一个有效数字可以包含五个部分:符号位、整数部分、小数点、小数部分、指数部分。其中除了符号位无条件非必须外,其他的部分存在一些制约条件:
- 小数点不能单独存在,只能和整数部分或者小数部分存在(分成两个状态,有整数小数点和无整数小数点)
- 指数部分不能单独存在,必须和前半有效部分存在,且指数的有效格式为指数字母加有效整数部分(指数符号、指数整数符号、指数整数部分)
这样的话,整个识别过程就变成了一个状态机(状态编号,当前状态(转移因素),可转移状态),加粗为有效终止态:
- 初始状态:符号位(符号)、整数部分(数字)、无整数小数点(小数点)
- 符号位:整数部分(数字)、无整数小数点(小数点)
- 整数部分:整数部分(数字)、有整数小数点(小数点)、指数符号(e或者E)
- 无整数小数点:小数部分(数字)
- 有整数小数点:指数符号(e或者E)、小数部分(数字)
- 小数部分:小数部分(数字)、指数符号(e或者E)
- 指数符号:指数整数符号(符号)、指数整数(数字)
- 指数整数符号:指数整数(数字)
- 指数整数:指数整数(数字)
按照上述状态转移记录进行模拟,即可在线性时间内,常数空间复杂度下完成判定,只要最终状态在其中一个有效终止态,当前输入就是有效数字。
因为我一直没有写过算法的状态机,而正则表达式太容易出错,这里我参考了官方题解的状态机写法。
- 时间复杂度: O ( N ) O(N) O(N),其中 N N N为输入的字符个数
- 空间复杂度: O ( 1 ) O(1) O(1),仅维护常数个状态变量
class Solution {
public:
bool isNumber(string s) {
int len = s.size();
State state= STATE_INITIAL;
for (auto c: s) {
Input input = getInput(c);
if (input == INPUT_OTHER) {
return false;
}
else if (F[state].find(input) == F[state].end()) {
return false;
}
else {
state = F[state][input];
}
}
return state == STATE_INTEGER || state == STATE_POINT || state == STATE_DECIMAL || state == STATE_EXP_INTEGER;
}
private:
enum State {
STATE_INITIAL,
STATE_SIGN,
STATE_INTEGER,
STATE_POINT_WITHOUT_INTEGER,
STATE_POINT,
STATE_DECIMAL,
STATE_EXP,
STATE_EXP_SIGN,
STATE_EXP_INTEGER
};
enum Input {
INPUT_SIGN,
INPUT_NUMBER,
INPUT_POINT,
INPUT_E,
INPUT_OTHER,
};
unordered_map<State, unordered_map<Input, State>> F{
{
STATE_INITIAL,
{
{INPUT_SIGN, STATE_SIGN},
{INPUT_NUMBER, STATE_INTEGER},
{INPUT_POINT, STATE_POINT_WITHOUT_INTEGER}
}
},
{
STATE_SIGN,
{
{INPUT_NUMBER, STATE_INTEGER},
{INPUT_POINT, STATE_POINT_WITHOUT_INTEGER}
}
},
{
STATE_INTEGER,
{
{INPUT_NUMBER, STATE_INTEGER},
{INPUT_POINT, STATE_POINT},
{INPUT_E, STATE_EXP}
}
},
{
STATE_POINT_WITHOUT_INTEGER,
{
{INPUT_NUMBER, STATE_DECIMAL}
}
},
{
STATE_POINT,
{
{INPUT_E, STATE_EXP},
{INPUT_NUMBER, STATE_DECIMAL}
}
},
{
STATE_DECIMAL,
{
{INPUT_NUMBER, STATE_DECIMAL},
{INPUT_E, STATE_EXP},
}
},
{
STATE_EXP,
{
{INPUT_SIGN, STATE_EXP_SIGN},
{INPUT_NUMBER, STATE_EXP_INTEGER},
}
},
{
STATE_EXP_SIGN,
{
{INPUT_NUMBER, STATE_EXP_INTEGER},
}
},
{
STATE_EXP_INTEGER,
{
{INPUT_NUMBER, STATE_EXP_INTEGER},
}
}
};
Input getInput(char c) {
if (c == '+' || c == '-') {
return INPUT_SIGN;
}
else if (c >= '0' && c <= '9') {
return INPUT_NUMBER;
}
else if (c == '.') {
return INPUT_POINT;
}
else if (c == 'e' || c == 'E') {
return INPUT_E;
}
else {
return INPUT_OTHER;
}
}
};
Solution 2
Solution 1的Python实现
from enum import Enum
class Solution:
State = Enum("State", [
"STATE_INITIAL",
"STATE_SIGN",
"STATE_INTEGER",
"STATE_POINT_WITHOUT_INTEGER",
"STATE_POINT",
"STATE_DECIMAL",
"STATE_EXP",
"STATE_EXP_SIGN",
"STATE_EXP_INTEGER",
])
Inputs = Enum("Inputs", [
"INPUT_SIGN",
"INPUT_NUMBER",
"INPUT_POINT",
"INPUT_E",
"INPUT_OTHER",
])
F = {
State.STATE_INITIAL: {
Inputs.INPUT_SIGN: State.STATE_SIGN,
Inputs.INPUT_NUMBER: State.STATE_INTEGER,
Inputs.INPUT_POINT: State.STATE_POINT_WITHOUT_INTEGER
},
State.STATE_SIGN: {
Inputs.INPUT_NUMBER: State.STATE_INTEGER,
Inputs.INPUT_POINT: State.STATE_POINT_WITHOUT_INTEGER
},
State.STATE_INTEGER: {
Inputs.INPUT_NUMBER: State.STATE_INTEGER,
Inputs.INPUT_POINT: State.STATE_POINT,
Inputs.INPUT_E: State.STATE_EXP
},
State.STATE_POINT_WITHOUT_INTEGER: {
Inputs.INPUT_NUMBER: State.STATE_DECIMAL
},
State.STATE_POINT: {
Inputs.INPUT_E: State.STATE_EXP,
Inputs.INPUT_NUMBER: State.STATE_DECIMAL
},
State.STATE_DECIMAL: {
Inputs.INPUT_NUMBER: State.STATE_DECIMAL,
Inputs.INPUT_E: State.STATE_EXP
},
State.STATE_EXP: {
Inputs.INPUT_SIGN: State.STATE_EXP_SIGN,
Inputs.INPUT_NUMBER: State.STATE_EXP_INTEGER
},
State.STATE_EXP_SIGN: {
Inputs.INPUT_NUMBER: State.STATE_EXP_INTEGER
},
State.STATE_EXP_INTEGER: {
Inputs.INPUT_NUMBER: State.STATE_EXP_INTEGER
},
}
@classmethod
def getInput(cls, c: str) -> Inputs:
if c == '+' or c == '-':
return Solution.Inputs.INPUT_SIGN
elif c.isdigit():
return Solution.Inputs.INPUT_NUMBER
elif c == '.':
return Solution.Inputs.INPUT_POINT
elif c.lower() == 'e':
return Solution.Inputs.INPUT_E
else:
return Solution.Inputs.INPUT_OTHER
def isNumber(self, s: str) -> bool:
state = Solution.State.STATE_INITIAL
for c in s:
print(state)
inp = Solution.getInput(c)
if inp == Solution.Inputs.INPUT_OTHER: return False
elif inp not in Solution.F[state]: return False
else: state = Solution.F[state][inp]
return state in [Solution.State.STATE_INTEGER, Solution.State.STATE_POINT, Solution.State.STATE_DECIMAL, Solution.State.STATE_EXP_INTEGER]