# 剑指 Offer 20. 表示数值的字符串——有限状态机

目录

  • 1.题目
  • 2.自我思路及实现
  • 3.总结思路及实现
  • 4.相关知识

1.题目

剑指 Offer 20. 表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100""5e2""-123""3.1416""-1E-16""0123"都表示数值,但"12e""1a3.14""1.2.3""+-5""12e+5.4"都不是。

经测验,5.0f,d,D等浮点数表示也不是
数字前后可能有空格
3.算数字

2.自我思路及实现

我们学习下其他人是怎么实现的吧!

3.总结思路及实现

1.标记+逐个遍历
设定三个标记表示数字、点、E正确地出现了
数字地正确存在为:是个数字
.的正确存在为:之前没有点且没有e
E的正确存在为:之前没有e且出现过数字,之后有数字
±的正确存在为:出现在开头或者前一位是e

时间:N
空间:1

class Solution {
     
    public boolean isNumber(String s) {
     
        if(s == null || s.length() == 0)
            return false;
        
        //处理开头结尾空格
        s = s.trim();
        //标记
        boolean numFlag = false;
        boolean dotFlag = false;
        boolean eFlag = false;

        for(int i = 0; i < s.length(); i++)
        {
     
            //判定为数字
            if(Character.isDigit(s.charAt(i)))
                numFlag = true;
            //判定为.,之前没有点且没有e
            else if(s.charAt(i) == '.' && !dotFlag && !eFlag)
                dotFlag = true;    
            //判定为e,之前没有e且出现过数字,之后有数字
            else if((s.charAt(i) == 'e' || s.charAt(i) == 'E') && !eFlag && numFlag)
            {
     
                eFlag = true;
                numFlag = false;
            }
            //判定为+-,在字符串开头或者前一个是e 
            else if((s.charAt(i) == '+' || s.charAt(i) == '-') && (i == 0 || s.charAt(i - 1) == 'e' || s.charAt(i - 1) == 'E'))
            {
     }
            else 
                return false;

        }
        //一定出现过数字
        return numFlag;

    }
}

2.组合判断
设一个数的表示为A.BeC或A.BEC,A为带或不带符号的整数,B为不带符号的整数或空,C为带或不带符号的整数
部分判断
时间N
空间1

class Solution {
     
    private int index = 0;
    private boolean isSignNum(String s)
    {
     
        if(s.charAt(index) == '+' || s.charAt(index) == '-')
            index ++;
        return isUnsignNum(s);
    }
    private boolean isUnsignNum(String s)
    {
     
        int pre = index;
        while(Character.isDigit(s.charAt(index)))
            index++;
        return pre < index;
    }
    public boolean isNumber(String s) {
     
        if(s == null || s.length() == 0)
            return false;
    //添加结束符    
    s = s + '|';
    //跳过开头空格
    while(s.charAt(index) == ' ')
        index++;
    //处理A部分
    boolean numFlag  = isSignNum(s);
    //处理点
    if(s.charAt(index) == '.')
    {
     
        index ++;
        //小数点两边至少有一个数字
        numFlag = isUnsignNum(s) || numFlag;//先处理B部分
    }
    //处理e和C,e两端均为数字
    if(s.charAt(index) == 'e' || s.charAt(index) == 'E')
    {
     
        index++;
        numFlag = numFlag && isSignNum(s);
    }
    //跳过结尾空格
    while(s.charAt(index) == ' ')
        index++;

    //结尾符号
    return numFlag && s.charAt(index) == '|';
    }
}

3.有限状态自动机
# 剑指 Offer 20. 表示数值的字符串——有限状态机_第1张图片
合法的结束状态为2、3、7、8
算法流程:
初始化:

状态转移表 statesstates : 设 states[i]states[i] ,其中 ii 为所处状态, states[i]states[i] 使用哈希表存储可转移至的状态。键值对 (key, value)(key,value) 含义:若输入 keykey ,则可从状态 ii 转移至状态 valuevalue 。
当前状态 pp : 起始状态初始化为 p = 0p=0 。
状态转移循环: 遍历字符串 ss 的每个字符 cc 。

记录字符类型 tt : 分为四种情况。
当 cc 为正负号时,执行 t = ‘s’ ;
当 cc 为数字时,执行 t = ‘d’ ;
当 cc 为 e , E 时,执行 t = ‘e’ ;
当 cc 为 . , 空格 时,执行 t = c (即用字符本身表示字符类型);
否则,执行 t = ‘?’ ,代表为不属于判断范围的非法字符,后续直接返回 falsefalse 。
终止条件: 若字符类型 tt 不在哈希表 states[p]states[p] 中,说明无法转移至下一状态,因此直接返回 FalseFalse 。
状态转移: 状态 pp 转移至 states[p][t]states[p][t] 。
返回值: 跳出循环后,若状态 p \in {2, 3, 7, 8}p∈2,3,7,8 ,说明结尾合法,返回 TrueTrue ,否则返回 FalseFalse 。

class Solution {
     
    public boolean isNumber(String s) {
     
        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 p = 0;//状态
        char t; //对应字符
        for(char c : s.toCharArray())
        {
     
            if(Character.isDigit(c))  t = 'd';
            else if(c == ' ') t = ' ';
            else if(c == '+' || c == '-') t = 's';
            else if(c == 'e' || c == 'E') t = 'e';
            else if(c == '.') t = '.';
            else 
                t = '?';
            if(!states[p].containsKey(t))
                return false;
            p = (int)states[p].get(t);
        }
        return p == 2 || p == 3 || p == 7 || p == 8;

    }
}

4.相关知识

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

有一个特殊的状态,被称作「初始状态」。
还有一系列状态被称为「接受状态」,它们组成了一个特殊的集合。其中,一个状态可能既是「初始状态」,也是「接受状态」。
起初,这个自动机处于「初始状态」。随后,它顺序地读取字符串中的每一个字符,并根据当前状态和读入的字符,按照某个事先约定好的「转移规则」,从当前状态转移到下一个状态;当状态转移完成后,它就读取下一个字符。当字符串全部读取完毕后,如果自动机处于某个「接受状态」,则判定该字符串「被接受」;否则,判定该字符串「被拒绝」。

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

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

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

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

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/solution/biao-shi-shu-zhi-de-zi-fu-chuan-by-leetcode-soluti/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(#,剑指Offer)