【剑指offer】20.表示数值的字符串 - 状态转移解法图解

题目描述

在这里插入图片描述

题目链接

LeetCode
acWing

解法一:状态转移

思路

对于一个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;  // 走到了结尾并且是有效的数字
    }
};

猜你喜欢

转载自blog.csdn.net/YanHS_/article/details/130406326