LeetCode 第65题:有效数字
题目描述
有效数字(按顺序)可以分成以下几个部分:
- 一个 小数 或者 整数
- (可选)一个
'e'或'E',后面跟着一个 整数
小数(按顺序)可以分成以下几个部分:
- (可选)一个符号字符(
'+'或'-') - 下述格式之一:
- 至少一位数字,后面跟着一个点
'.' - 至少一位数字,后面跟着一个点
'.',后面再跟着至少一位数字 - 一个点
'.',后面跟着至少一位数字
- 至少一位数字,后面跟着一个点
整数(按顺序)可以分成以下几个部分:
- (可选)一个符号字符(
'+'或'-') - 至少一位数字
部分有效数字列举如下:["2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1", "53.5e93", "-123.456e789"]
部分无效数字列举如下:["abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"]
给你一个字符串 s ,如果 s 是一个 有效数字 ,请返回 true 。
难度
困难
题目链接
示例
示例 1:
输入:s = "0" 输出:true
示例 2:
输入:s = "e" 输出:false
示例 3:
输入:s = "." 输出:false
示例 4:
输入:s = ".1" 输出:true
提示
1 <= s.length <= 20s仅含英文字母(大写和小写),数字(0-9),加号'+',减号'-',或者点'.'
解题思路
状态机解法
这道题可以使用有限状态机(DFA)来解决。我们需要定义不同的状态和状态转移规则。
关键点:
- 定义所有可能的状态
- 明确状态转移规则
- 确定合法的终止状态
- 处理所有可能的输入字符
具体步骤:
- 定义状态转移表
- 遍历字符串的每个字符
- 根据当前状态和字符进行状态转移
- 检查最终状态是否合法
图解思路
算法步骤分析表
| 状态 | 说明 | 可接受字符 | 下一状态 |
|---|---|---|---|
| 初始 | 开始状态 | +/- | 符号状态 |
| 符号 | 符号后 | 数字/点 | 整数/小数 |
| 整数 | 整数部分 | 数字/点/e | 整数/小数/指数 |
| 小数 | 小数部分 | 数字/e | 小数/指数 |
| 指数 | 指数部分 | +/-/数字 | 指数符号/指数数字 |
状态/情况分析表
| 情况 | 输入 | 输出 | 说明 |
|---|---|---|---|
| 整数 | "123" | true | 基本情况 |
| 小数 | "0.1" | true | 带小数点 |
| 科学计数 | "1e10" | true | 带指数 |
| 非法 | "abc" | false | 含字母 |
代码实现
C# 实现
public class Solution {
public bool IsNumber(string s) {
int state = 0;
bool[] finals = {false, false, true, true, false, true, true, false, true};
int[][] transfer = new int[][]{
new int[] {0, 1, 6, 2, -1}, // 0 初始状态
new int[] {-1, -1, 6, 2, -1}, // 1 符号状态
new int[] {-1, -1, 3, -1, -1}, // 2 小数点前的整数
new int[] {8, -1, 3, -1, 4}, // 3 小数点后的整数
new int[] {-1, 7, 5, -1, -1}, // 4 指数符号e
new int[] {8, -1, 5, -1, -1}, // 5 指数后的整数
new int[] {8, -1, 6, 3, 4}, // 6 小数点前的整数
new int[] {-1, -1, 5, -1, -1}, // 7 指数后的符号
new int[] {8, -1, -1, -1, -1} // 8 结束状态
};
foreach (char c in s) {
int id = GetInputId(c);
if (id < 0) return false;
state = transfer[state][id];
if (state < 0) return false;
}
return finals[state];
}
private int GetInputId(char c) {
if (c == ' ') return 0;
if (c == '+' || c == '-') return 1;
if (c >= '0' && c <= '9') return 2;
if (c == '.') return 3;
if (c == 'e' || c == 'E') return 4;
return -1;
}
}
Python 实现
class Solution:
def isNumber(self, s: str) -> bool:
# 定义状态转移表
transfer = {
0: {' ': 0, 'sign': 1, 'digit': 6, '.': 2},
1: {'digit': 6, '.': 2},
2: {'digit': 3},
3: {'digit': 3, 'e': 4},
4: {'sign': 7, 'digit': 5},
5: {'digit': 5},
6: {'digit': 6, '.': 3, 'e': 4},
7: {'digit': 5},
8: {' ': 8}
}
# 定义终止状态
finals = {3, 5, 6, 8}
def get_char_type(c: str) -> str:
if c.isspace():
return ' '
if c in ['+', '-']:
return 'sign'
if c.isdigit():
return 'digit'
if c == '.':
return '.'
if c in ['e', 'E']:
return 'e'
return 'unknown'
# 初始状态
state = 0
for c in s:
char_type = get_char_type(c)
if char_type == 'unknown':
return False
if char_type not in transfer[state]:
return False
state = transfer[state][char_type]
return state in finals
C++ 实现
class Solution {
public:
bool isNumber(string s) {
vector<bool> finals = {false, false, true, true, false, true, true, false, true};
vector<vector<int>> transfer = {
{0, 1, 6, 2, -1}, // 0 初始状态
{-1, -1, 6, 2, -1}, // 1 符号状态
{-1, -1, 3, -1, -1}, // 2 小数点前的整数
{8, -1, 3, -1, 4}, // 3 小数点后的整数
{-1, 7, 5, -1, -1}, // 4 指数符号e
{8, -1, 5, -1, -1}, // 5 指数后的整数
{8, -1, 6, 3, 4}, // 6 小数点前的整数
{-1, -1, 5, -1, -1}, // 7 指数后的符号
{8, -1, -1, -1, -1} // 8 结束状态
};
int state = 0;
for (char c : s) {
int id = getInputId(c);
if (id < 0) return false;
state = transfer[state][id];
if (state < 0) return false;
}
return finals[state];
}
private:
int getInputId(char c) {
if (c == ' ') return 0;
if (c == '+' || c == '-') return 1;
if (isdigit(c)) return 2;
if (c == '.') return 3;
if (c == 'e' || c == 'E') return 4;
return -1;
}
};
执行结果
- 执行用时:76 ms
- 内存消耗:37.1 MB
代码亮点
- 🎯 使用状态机清晰处理各种情况
- 💡 状态转移表设计合理
- 🔍 代码结构清晰,易于维护
- 🎨 优雅处理复杂的判断逻辑
常见错误分析
- 🚫 没有处理所有可能的输入字符
- 🚫 状态转移规则不完整
- 🚫 终止状态判断错误
- 🚫 特殊情况处理不当
解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 正则表达式 | O(n) | O(1) | 代码简短 | 可读性差 |
| 条件判断 | O(n) | O(1) | 直观易懂 | 逻辑复杂 |
| 状态机 | O(n) | O(1) | 结构清晰 | 实现复杂 |
相关题目
- 字符串转换整数 (atoi) - 中等
- 验证IP地址 - 中等
- DFA设计 - 困难