剑指 Offer 20题. 表示数值的字符串

1.题目描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。

数值(按顺序)可以分成以下几个部分:

  1. 若干空格
  2. 一个 小数 或者 整数
  3. (可选)一个 'e' 或 'E' ,后面跟着一个 整数
  4. 若干空格

小数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+' 或 '-')
  2. 下述格式之一:
  3. 至少一位数字,后面跟着一个点 '.'
  4. 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
  5. 一个点 '.' ,后面跟着至少一位数字

整数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+' 或 '-')
  2. 至少一位数字

部分数值列举如下:

["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]

 部分非数值列举如下:

["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]

示例 1:

输入:s = "0"
输出:true 


示例 2:

输入:s = "e"
输出:false 


示例 3:

输入:s = "."
输出:false 


示例 4:

输入:s = "    .1  "
输出:true 

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof

2.思路分析 

本题使用有限状态自动机。根据字符类型和合法数值的特点,先定义状态,再画出状态转移图,最后编写代码即可。

有限状态自动机(以下简称「自动机」)是一类计算模型。它包含一系列状态,这些状态中:

  • 有一个特殊的状态,被称作「初始状态」。
  • 还有一系列状态被称为「接受状态」,它们组成了一个特殊的集合。其中,一个状态可能既是「初始状态」,也是「接受状态」。

起初,这个自动机处于「初始状态」。随后,它顺序地读取字符串中的每一个字符,并根据当前状态和读入的字符,按照某个事先约定好的「转移规则」,从当前状态转移到下一个状态;当状态转移完成后,它就读取下一个字符。当字符串全部读取完毕后,如果自动机处于某个「接受状态」,则判定该字符串「被接受」;否则,判定该字符串「被拒绝」。

注意:如果输入的过程中某一步转移失败了,即不存在对应的「转移规则」,此时计算将提前中止。在这种情况下我们也判定该字符串「被拒绝」。

一个自动机,总能够回答某种形式的「对于给定的输入字符串 S,判断其是否满足条件 P」的问题。在本题中,条件 P 即为「构成合法的表示数值的字符串」。

自动机驱动的编程,可以被看做一种暴力枚举方法的延伸:它穷尽了在任何一种情况下,对应任何的输入,需要做的事情。

自动机在计算机科学领域有着广泛的应用。在算法领域,它与大名鼎鼎的字符串查找算法「KMP」算法有着密切的关联;在工程领域,它是实现「正则表达式」的基础。

2.1 字符类型:

空格 「 」、数字「 0—9 」 、正负号 「+− 」 、小数点 「 . 」 、幂符号 「 eE 」 。

2.2 状态定义:

按照字符串从左到右的顺序,定义以下 9 种状态。

  1. 开始的空格
  2. 幂符号前的正负号
  3. 小数点前的数字
  4. 小数点、小数点后的数字
  5. 当小数点前为空格时,小数点、小数点后的数字
  6. 幂符号
  7. 幂符号后的正负号
  8. 幂符号后的数字
  9. 结尾的空格

2.3 结束状态:

合法的结束状态有 2, 3, 7, 8 。

3.解答

class Solution {
    //有限状态自动机

    //定义状态:
    /* 按照字符串从左到右的顺序,定义以下 9 种状态。
        0.开始的空格
        1.幂符号前的正负号
        2.小数点前的数字
        3.小数点、小数点后的数字
        4.当小数点前为空格时,小数点、小数点后的数字
        5.幂符号
        6.幂符号后的正负号
        7.幂符号后的数字
        8.结尾的空格
    */

    public boolean isNumber(String s) {
        //用map结构的数组存储有限的状态
        Map[] states = {
            new HashMap<>() {{ put(' ', 0); put('s', 1); put('d', 2); put('.', 4); }}, // 0.
            new HashMap<>() {{ put('d', 2); put('.', 4); }},                           // 1.
            new HashMap<>() {{ put('d', 2); put('.', 3); put('e', 5); put(' ', 8); }}, // 2.
            new HashMap<>() {{ put('d', 3); put('e', 5); put(' ', 8); }},              // 3.
            new HashMap<>() {{ put('d', 3); }},                                        // 4.
            new HashMap<>() {{ put('s', 6); put('d', 7); }},                           // 5.
            new HashMap<>() {{ put('d', 7); }},                                        // 6.
            new HashMap<>() {{ put('d', 7); put(' ', 8); }},                           // 7.
            new HashMap<>() {{ put(' ', 8); }}                                         // 8.
        };

        //表示当前所处的状态位置
        int position = 0;
        //表示当前字符的类型
        char type;

        //循环判断每一个字符
        for(char c : s.toCharArray()){
            if(c == '+' || c == '-'){
                type = 's'; //sign表示正负号
            }else if(c >= '0' && c <= '9'){
                type = 'd'; //digit表示整数
            }else if(c == 'e' || c == 'E'){
                type = 'e'; //e表示e或者E
            }else if(c == ' ' || c == '.'){
                type = c; //c表示空格或者小数点
            }else{
                type = '?'; //表示未匹配字符
            }
            
            //判断当前字符c的类型t是否合法
            if(!states[position].containsKey(type)){
                return false;
            }
            //状态转移
            position = (int)states[position].get(type);
        }

        //只有以状态2378结尾才是合法数值
        return position == 2 || position == 3 || position == 7 || position == 8;
    }
}
  • 时间复杂度 O(N) : 其中 N 为字符串 s 的长度,判断需遍历字符串,每轮状态转移的使用 O(1) 时间。
  • 空间复杂度 O(1) : states 和 p 使用常数大小的额外空间。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值