本篇博客由力扣第8题引出,这类题目倒是不难,但在不了解自动机之前写出的代码非常臃肿,暴力解需要考虑大量的细节,需要大量的if-else,用了自动机之后才知道什么叫优雅的代码。
题目介绍:
8. 字符串转换整数 (atoi)
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
- 如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
- 返回整数作为最终结果。
注意:
- 本题中的空白字符只包括空格字符 ’ ’ 。
- 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
- 0 <= s.length <= 200
- s 由英文字母(大写和小写)、数字(0-9)、’ ‘、’+’、’-’ 和 ‘.’ 组成
示例 :
输入:s = "42"
输出:42
输入:s = " -42"
输出:-42
输入:s = "4193 word 98"
输出:4193
输入:s = "words and 987"
输出:0
解题思路
自动机的关键在于找准状态转移关系,即找出在每种状态下遇到各种事件之后的下一个状态。
经分析,该题中可以定义四种状态:start、signed、inNum、end;可以定义四种事件:空字符、‘+‘/’-‘、数字、其它;则状态转移关系如下:
当前状态 / 事件 | 空字符 | ‘+‘/’-‘ | 数字 | 其它 |
---|---|---|---|---|
start | start | signed | inNum | end |
stated | end | end | inNum | end |
inNum | end | end | inNum | end |
end | end | end | end | end |
将上述思路写成代码,则我们的自动机类为:
class AutoMachine{
String state;//状态
int sign;//符号
long value;//数值
HashMap<String,String[]>map;//存储状态转移关系
public AutoMachine(){
map=new HashMap<>();
// '' +/- num else
//start start signed inNum end
//signed end end inNum end
//inNum end end inNum end
//end end end end end
map.put("start",new String[]{"start","signed","inNum","end"});
map.put("signed",new String[]{"end","end","inNum","end"});
map.put("inNum",new String[]{"end","end","inNum","end"});
map.put("end",new String[]{"end","end","end","end"});
state="start";
sign=1;
value=0;
}
public void get(char c){
//根据所处状态和给定的字符从map中获取下一状态
state=map.get(state)[getcol(c)];
if(state.equals("signed")){
sign=c=='+'?1:-1;
}else if(state.equals("inNum")){
value=value*10+(c-'0');
//防止溢出
value=sign==1?Math.min(value,Integer.MAX_VALUE):Math.min(value,-(long)Integer.MIN_VALUE);
}
}
private int getcol(char c){
//事件3
if(Character.isDigit(c)){
return 2;
}
switch (c){
case ' ': //事件1
return 0;
case '+':
case '-': //事件2
return 1;
default: //事件4
return 3;
}
}
}
有了这个自动机,我们的主方法就会变得非常简洁。
public int myAtoi(String str) {
int len=str.length();
AutoMachine am=new AutoMachine();
for(int i=0;i<len;i++){
am.get(str.charAt(i));
}
return (int)am.value*am.sign;
}