剑指 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
}
}