题目描述
题目链接
解法一:状态转移
思路
对于一个buff拉满的数字,可能有以下的几个部分组成
空格 符号 整数数字 小数点 小数数字 E 符号 指数数字 空格
定义以下状态
编号 | 定义 |
---|---|
0 | 起始的空格 |
1 | 符号位 |
2 | 整数数字 |
3 | 整数部分有数字时候的小数点 |
4 | 整数部分没有数字时候的小数点 |
5 | 小数数字 |
6 | e或者E |
7 | 指数的符号 |
8 | 指数数字 |
9 | 最后的空格 |
对输入的字符也做一下编码
编号 | 定义 |
---|---|
0 | 非法字符 |
1 | 空格 |
2 | 符号 |
3 | 数字 |
4 | 小数点 |
5 | e或者E |
则可以得到一个如下的状态转移图
通过编码上面的图,可以得到一个状态转移表
0是起始状态,-1是非法状态
当前状态 | 输入0 | 输入1 | 输入2 | 输入3 | 输入4 | 输入5 |
---|---|---|---|---|---|---|
0 | -1 | 0 | 1 | 2 | 4 | -1 |
1 | -1 | -1 | -1 | 2 | 4 | -1 |
2 | -1 | 9 | -1 | 2 | 3 | 6 |
3 | -1 | 9 | -1 | 5 | -1 | 6 |
4 | -1 | -1 | -1 | 5 | -1 | -1 |
5 | -1 | 9 | -1 | 5 | -1 | 6 |
6 | -1 | -1 | 7 | 8 | -1 | -1 |
7 | -1 | -1 | -1 | 8 | -1 | -1 |
8 | -1 | 9 | -1 | 8 | -1 | -1 |
9 | -1 | 9 | -1 | -1 | -1 | -1 |
以上状态中,有效状态(最终转移结束后,是合法数据的状态)有2(只有整数)、3(整数+小数点,没有小数部分)、5(整数+小数点+小数部分组成的小数)、8(合法的科学计数法输入)、9(8状态加上后续的空格)
这道题目的状态数量和输入数量都不大,所以用了一个二维数组来存储。一般可以使用哈希表存储有效的状态转移,对于查询不到的转移路径就是非法路径。
则我们从状态0开始,按照输入依次转移,如果转移到了非法状态(使用哈希表表示转移路径的时候则是转移路径不存在),则直接返回false
。如果转移正常结束,且最终的状态有效(属于2、3、5、8、9之一),则返回true
,否则返回false
。
C++代码
class Solution {
private:
int state[10][6] =
{
{-1, 0, 1, 2, 4, -1},
{-1, -1, -1, 2, 4, -1},
{-1, 9, -1, 2, 3, 6},
{-1, 9, -1, 5, -1, 6},
{-1, -1, -1, 5, -1, -1},
{-1, 9, -1, 5, -1, 6},
{-1, -1, 7, 8, -1, -1},
{-1, -1, -1, 8, -1, -1},
{-1, 9, -1, 8, -1, -1},
{-1, 9, -1, -1, -1, -1},
};
int char2num(char c)
{
if (c==' ') return 1; // 空格
else if (c=='+'||c=='-') return 2; // 符号
else if (c>='0'&&c<='9') return 3; // 数字
else if (c=='.') return 4; // 小数点
else if (c=='e'||c=='E') return 5; // E
else return 0; // 非法字符
}
public:
bool isNumber(string s)
{
int st=0;
for(int i=0;i<s.size();i++)
{
int c = char2num(s[i]);
st = state[st][c];
if (st==-1) return false;
}
if (st==2||st==3||st==5||st==8||st==9) return true;
else return false;
}
};
解法二:指针移动
思路
其实和状态转移的想法有些像,先列出来可能出现的合法字符情况,使用一个指针指向字符串的开始,然后依次按合法的输入去判断,当前字符的输入以后,如果当前字符串是某个合法字符串的前缀(整个字符串可能是合法的,而不是指当前就是合法的,比如“12e”是非法的,但是它是“12e3”这个合法字符串的前缀,那么仍然成立),就把指针向后移动。
因为移动过程中可能会出现当前字符串无效的情况,所以使用一个布尔变量flag记录当前字符串是否是有效数字表示。
在流程里面,只按有效输入的路径让指针往后走,如果字符串里面当前字符是无效的,那么指针就会走不动。
最后如果指针可以走完整个字符串并且flag有效,那个字符串就是有效的。
C++代码
class Solution {
public:
bool isNumber(string s)
{
s += '\0'; // 添加一个结尾字符,也可以转换成c类型的字符串
bool flag = false; // 初始的时候是无效的
int i = 0;
while(s[i]==' ') i++; // 开头的空格
// 下面开始e之前的数字部分
// 符号要在最前面
// 有且仅可以有一个小数点(只判断一次'.')
// 小数点前后至少有一个数字(有数字的时候就置flag为true)
if(s[i]=='-'||s[i]=='+') i++; // 符号位
while(s[i]<='9'&&s[i]>='0') // 整数部分
{
i++;
flag = true;
}
if(s[i]=='.') i++; // 小数点
while(s[i]<='9'&&s[i]>='0') // 小数部分的数字
{
i++;
flag = true; // 如果没有整数部分,有小数点且有小数部分也是有效的
}
// 下面开始判断e之后的
// 在e之后,可以有一个符号位
// 在出现数字之前,是无效的,需要置flag为false
// 出现数字之后是有效的,置true
if (flag && (s[i]=='e'||s[i]=='E')) // 前面的要是一个有效的数字,并且当前字符是e
{
i++;
flag = false; // 只有e的时候不是有效的数字
// 下面的部分要在if里面才成立
if(s[i]=='-'||s[i]=='+') i++;
while(s[i]<='9'&&s[i]>='0')
{
i++;
flag = true; // e后面跟上了数字,是有效的数字表示
}
}
while(s[i]==' ') i++; // 结尾的空格
return s[i]=='\0' && flag; // 走到了结尾并且是有效的数字
}
};