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

文章介绍了两种解决LeetCode上关于判断字符串是否为有效数字的方法。第一种是状态转移,通过建立状态转移图和二维数组来判断输入字符串的合法性。第二种是通过指针移动,检查字符串中各个部分是否符合有效数字的构成规则。两种方法都关注字符串中的符号、整数、小数点、指数等关键部分。
摘要由CSDN通过智能技术生成

题目描述

在这里插入图片描述

题目链接

LeetCode
acWing

解法一:状态转移

思路

对于一个buff拉满的数字,可能有以下的几个部分组成

空格 符号 整数数字 小数点 小数数字 E 符号 指数数字 空格

定义以下状态

编号定义
0起始的空格
1符号位
2整数数字
3整数部分有数字时候的小数点
4整数部分没有数字时候的小数点
5小数数字
6e或者E
7指数的符号
8指数数字
9最后的空格

对输入的字符也做一下编码

编号定义
0非法字符
1空格
2符号
3数字
4小数点
5e或者E

则可以得到一个如下的状态转移图
在这里插入图片描述
通过编码上面的图,可以得到一个状态转移表

0是起始状态,-1是非法状态

当前状态输入0输入1输入2输入3输入4输入5
0-10124-1
1-1-1-124-1
2-19-1236
3-19-15-16
4-1-1-15-1-1
5-19-15-16
6-1-178-1-1
7-1-1-18-1-1
8-19-18-1-1
9-19-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;  // 走到了结尾并且是有效的数字
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值