剑指 Offer 20. 表示数值的字符串(Java题解)

剑指 Offer 20. 表示数值的字符串(Java题解)


题目描述

在这里插入图片描述
在这里插入图片描述

预备知识

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

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

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

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

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

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

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/solution/biao-shi-shu-zhi-de-zi-fu-chuan-by-leetcode-soluti/
来源:力扣(LeetCode)


分析模型

我们要画出判断字符串是数值的模型,如下图:

在这里插入图片描述

五种颜色的线条代表的是五种转换规则:

  • 遇到空格时
  • 遇到数字时
  • 遇到小数点时
  • 遇到字符e时
  • 遇到+/-号时

十个矩形框代表十种状态:

  • 起始空格
  • 符号位
  • 小数点(左边无整数)
  • 整数
  • 小数点
  • 小数部分
  • 字符e
  • 指数符号
  • 指数数字
  • 末尾空格

不难看出,其实就是手动实现一个正则表达式。

题解

利用模型图,可以轻松写出题解,AC代码如下:

class Solution {
    public boolean isNumber(String s) {
        Map<State,Map<CharType,State>> transfer = new HashMap<State,Map<CharType,State>>();

        //初始状态转换规则
        Map<CharType,State> initialMap = new HashMap<CharType,State>(){{
            put(CharType.CHAR_NUM,State.STATE_INTEGER);
            put(CharType.CHAR_POINT, State.STATE_POINT_NO_INT);
            put(CharType.CHAR_SPACE,State.STATE_INITIAL);
            put(CharType.CHAR_SIGN,State.STATE_INT_SIGN);
        }};
        transfer.put(State.STATE_INITIAL,initialMap);

        //到符号位之后转换规则
        Map<CharType,State> intSignMap = new HashMap<CharType,State>(){{
           put(CharType.CHAR_POINT,State.STATE_POINT_NO_INT);
           put(CharType.CHAR_NUM,State.STATE_INTEGER);
        }};
        transfer.put(State.STATE_INT_SIGN,intSignMap);

        //到整数位之后的转换规则
        Map<CharType,State> integerMap = new HashMap<CharType,State>(){{
           put(CharType.CHAR_NUM,State.STATE_INTEGER);
           put(CharType.CHAR_POINT,State.STATE_POINT);
           put(CharType.CHAR_EXP,State.STATE_EXP);
           put(CharType.CHAR_SPACE,State.STATE_END);
        }};
        transfer.put(State.STATE_INTEGER,integerMap);

        //到小数点位之后的转换规则
        Map<CharType,State> pointMap = new HashMap<CharType,State>(){{
            put(CharType.CHAR_NUM,State.STATE_DECIMAL_PART);
            put(CharType.CHAR_EXP,State.STATE_EXP);
            put(CharType.CHAR_SPACE,State.STATE_END);
        }};
        transfer.put(State.STATE_POINT,pointMap);

        //到小数点前无整数位之后的转换规则
        Map<CharType,State> pointNoIntMap = new HashMap<CharType,State>(){{
            put(CharType.CHAR_NUM,State.STATE_DECIMAL_PART);
        }};
        transfer.put(State.STATE_POINT_NO_INT,pointNoIntMap);

        //到小数部分位之后的转换规则
        Map<CharType,State> decimalPartMap = new HashMap<CharType,State>(){{
            put(CharType.CHAR_NUM,State.STATE_DECIMAL_PART);
            put(CharType.CHAR_EXP,State.STATE_EXP);
            put(CharType.CHAR_SPACE,State.STATE_END);
        }};
        transfer.put(State.STATE_DECIMAL_PART,decimalPartMap);

        //到字符e位之后的转换规则
        Map<CharType,State> expMap = new HashMap<CharType,State>(){{
            put(CharType.CHAR_SIGN,State.STATE_EXP_SIGN);
            put(CharType.CHAR_NUM,State.STATE_EXP_NUM);
        }};
        transfer.put(State.STATE_EXP,expMap);

        //到指数符号位之后的转换规则
        Map<CharType,State> expSignMap = new HashMap<CharType,State>(){{
            put(CharType.CHAR_NUM,State.STATE_EXP_NUM);
        }};
        transfer.put(State.STATE_EXP_SIGN,expSignMap);

        //到指数数字位之后的转换规则
        Map<CharType,State> expNumMap = new HashMap<CharType,State>(){{
            put(CharType.CHAR_NUM,State.STATE_EXP_NUM);
            put(CharType.CHAR_SPACE,State.STATE_END);
        }};
        transfer.put(State.STATE_EXP_NUM,expNumMap);

        //到结尾空格位之后的转换规则
        Map<CharType,State> endMap = new HashMap<CharType,State>(){{
            put(CharType.CHAR_SPACE,State.STATE_END);
        }};
        transfer.put(State.STATE_END,endMap);


        //开始转换
        int length = s.length();
        State myState = State.STATE_INITIAL;
        for (int i = 0; i < length; i++) {
            CharType type = toCharType(s.charAt(i));
            if (!transfer.get(myState).containsKey(type)){
                return false;
            }else {
                myState = transfer.get(myState).get(type);
            }
        }

        //最后通过myState判断是否满足数值要求
        return  (myState==State.STATE_INTEGER || myState==State.STATE_POINT || myState==State.STATE_DECIMAL_PART || myState==State.STATE_EXP_NUM || myState==State.STATE_END);
    }


    //将字符转换为转移规则
    public static CharType toCharType(char ch){
        if (ch>='0' && ch<='9') {
            return CharType.CHAR_NUM;
        } else if (ch=='+' || ch=='-') {
            return CharType.CHAR_SIGN;
        } else if (ch=='E' || ch=='e') {
            return CharType.CHAR_EXP;
        } else if (ch=='.') {
            return CharType.CHAR_POINT;
        } else if (ch==' ') {
            return CharType.CHAR_SPACE;
        }else {
            return CharType.CHAR_ERROR;
        }
    }

    //状态
    enum State {
        STATE_INITIAL,
        STATE_INT_SIGN,
        STATE_INTEGER,
        STATE_POINT,
        STATE_POINT_NO_INT,
        STATE_DECIMAL_PART,
        STATE_EXP,
        STATE_EXP_SIGN,
        STATE_EXP_NUM,
        STATE_END

    }

    //字符类型
    enum CharType {
        CHAR_SPACE,
        CHAR_NUM,
        CHAR_POINT,
        CHAR_EXP,
        CHAR_SIGN,
        CHAR_ERROR
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

压力小子呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值