算法总结:高手们常说的DFA(自动机)算法是什么
简介
自动机编程(英语:Automata-based programming)
是编程典范中的一种,是指程式或其中的部份是以有限状态机(FSM)为模型的程式,有些程式则会用其他型式(也更复杂)的自动机为其模型。自动机程序在每个时刻有一个状态 s,每次经过一个行动 f,转移到下一个状态 s’。这样,只需要建立一个覆盖所有情况的从 s 与 f 映射到 s’ 的转移规则即可解决问题。
一:从一个C++语言程序开始
要求写一个C++程序,对一个文本资料一行一行的读取,并输出每一行的第一个英文字符。对第一个英文字符之前的空白字符,若有,需读取所有空白后略过,读取第一个英文单字然后输出,之后读取其他内容略过不输出,直到读到换行符号为止,任何情形下只要读到档案结束(end-of-file)的符号,就结束程序。
1.基础C语言解法
思路:使用三个循环,分别对应于getchar到的字符的类型,执行不同的行动
题解:
int mycode(char c){
bool flag=true;
while(c!= EOF){
c=getchar();
while(isspace(c)) c=getchar();
while(isalpha(c)){
if(flag=true){
cout<<c<<endl;
flag=false;
}
c=getchar();
}
while(c=='\n'){
flag=true;
c=getchar();
}
}
return0;
}
评价:
这种解法在简单问题上是非常简洁明了的,但面对复杂、工程化的问题,我们还有另一种解法:自动机算法
2.DFA(自动机)算法思想
上述问题也可以用有有限状态机的方式处理,此程式有三个不同的阶段:读取并跳过第一个单字前的空白、读取第一个单字并且打印、跳过后续的所有字符直到换行符。以下将这三个阶段定义为三个状态start、process及after。自动机程序在每个时刻有一个状态 s,每次从资料中读取一个字符 c,并根据字符 c 转移到下一个状态 s’。这样,我们只需要建立一个覆盖所有情况的从 s 与 c 映射到 s’ 的表格即可解决题目中的问题。
表示这个自动机的状态转换过程的状态转换表如下:
状态\字符 | 空格符 | Alpha | 换行符 |
---|---|---|---|
start | start | process | after |
process | process | process | after |
after | start | process | after |
接下来编程部分就非常简单了:我们只需要把上面这个状态转换表写成代码即可,自动机编程的题解如下:
3.自动机编程题解
DFA算法题解:
class Mycode {
string state = "start";
//使用hashmap存储状态转换表
unordered_map<string, vector<string>> table = {
{"start", {"start", "process", "after"}},
{"process", {"process", "process", "after"}},
{"after", {"start", "process", "after"}}
};
//定义状态转移规则
int ifc(char c) {
if (isspace(c)) return 0;
if (isalpha(c)) return 1;
if (c=='\n')) return 2;
}
public:
//定义一个flag初始化为true当进入新的一行后重新设置为true
bool flag=true;
void get(char c) {
state = table[state][ifc(c)];
if (state == "process") {
if(flag==true) cout<<c<<endl;
//当输出一个英文字母后设置flag为flase
flag=false;
}
else if (state == "after")
//当进入新的一行后重新设置flag为true
flag=true;
}
};
//主循环
int main{
char c;
while(c != EOF){
c =getchar();
Mycode mycode;
mycode.get(c);
}
return 0;
}
评价:虽然此程序较长,但是主程序中只调用一个mycode.get()函数,而且程式中只有一个循环,不像之前程式使用四个循环。函数main中的while循环内即为自动机的步骤,而循环本身即可重复的执行自动机的程序。自动机编程可视为一种特殊的指令式编程,其显式的状态减少到最少,因此二个不同时间点的程式的差异只在自动机状态的不同,因此可以简化程式的分析。其编程思想非常值得借鉴。
二:Leetcode实战
Leetcode第八题-字符串转换整数 (atoi)
题目描述:
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:
1.读入字符串并丢弃无用的前导空格
2.检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
3.读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
4.将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
5.如果整数数超过 32 位有符号整数范围 [−2^31, 2^31 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −2^31 的整数应该被固定为 −2^31 ,大于 2^31 − 1 的整数应该被固定为 2^31 − 1 。
6.返回整数作为最终结果。
注意:
本题中的空白字符只包括空格字符 ’ ’ 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
示例1:
输入:s = “words and 987”
输出:0
解释:
第 1 步:“words and 987”(当前没有读入字符,因为没有前导空格)
第 2 步:“words and 987”(当前没有读入字符,因为这里不存在 ‘-’ 或者 ‘+’)
第 3 步:“words and 987”(由于当前字符 ‘w’ 不是一个数字,所以读入停止)
解析得到整数 0 ,因为没有读入任何数字。
由于 0 在范围 [-231, 231 - 1] 内,最终结果为 0。
示例2:
输入:s = “-91283472332”
输出:-2147483648
解释:
由于 -91283472332 小于范围 [-2^31, 2^31 - 1] 的下界,最终结果输出为最小数 -2^31 = -2147483648
DFA算法题解:
class Mycode {
string state = "start";
//使用hashmap存储状态转换表
unordered_map<string, vector<string>> table = {
{"start", {"start", "negative", "process", "end"}},
{"negative", {"end", "end", "process", "end"}},
{"process", {"end", "end", "process", "end"}},
{"end", {"end", "end", "end", "end"}}
};
//定义状态转移规则
int ifc(char c) {
if (c==" ") return 0;
if (c == '+' or c == '-') return 1;
if (isdigit(c)) return 2;
return 3;
}
public:
int sign = 1;
long long res = 0;
void get(char c) {
state = table[state][ifc(c)];
if (state == "process") {
//减48实现char数字到int数字的转换
ans = ( ans * 10 + c ) - 48;
//判断是否越界,比较时需要将INT_MAX转换成long long类型
ans = ( sign == 1 ) ? min(ans, (long long)INT_MAX) : min(ans, -(long long)INT_MIN);
}
else if (state == "negative")
sign = c == '+' ? 1 : -1;
}
};
class Solution {
public:
int myAtoi(string str) {
Mycode mycode;
for (char c : str)
mycode.get(c);
return mycode.sign * mycode.ans;
}
};
♥觉得有用,给个赞吧♥
注
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/string-to-integer-atoi
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
博客著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。