作者最近在leetcode上遇到了可以用有限状态自动机来解决的题目,在此记录自动状态机思想及其应用。
有限状态自动机(以下简称「自动机」)是一类计算模型。它包含一系列状态,这些状态中:有一种特殊的状态,被称作「初始状态」。还有一系列状态被称为「接受状态」,它们组成了一个特殊的集合。其中,一个状态可能既是「初始状态」,也是「接受状态」。
起初,这个自动机处于「初始状态」。随后,它顺序地读取字符串中的每一个字符,并根据当前状态和读入的字符,按照某个事先约定好的「转移规则」,从当前状态转移到下一个状态;当状态转移完成后,它就读取下一个字符。当字符串全部读取完毕后,如果自动机处于某个「接受状态」,则判定该字符串「被接受」;否则,判定该字符串「被拒绝」。
我们可以将各种状态按照其规定的转移规则写入一个表格中,用map来实现该表格。于是,我们便可以通过访问key(当前状态),找到value(下一个状态)。
题目:
此类关于字符串处理的题目一般都具有复杂的条件判断和流程,手写if-else语句会非常麻烦。所以我们考虑用有限状态自动机,一个字符一个字符的进行处理。
首先,我们根据题目要求画出不同状态之间的关系图:
然后再将此关系图整理列为表格:
然后我们再通过map对该表格进行储存,其实现代码如下:
string state = "start"; //初始state为“start”
unordered_map<string,vector<string>> table ={
{"start",{"start","signed","in_number","end"}},
{"signed",{"end","end","in_number","end"}},
{"in_number",{"end","end","in_number","end"}},
{"end",{"end","end","end","end"}}
};
//使用unordered_map更高效地查找元素
然后,我们需要设计一个函数来判断当前字符应该对应哪一种状态
int get_col(char c){
if(isspace(c))
return 0;
if(c=='+' || c=='-')
return 1;
if(isdigit(c))
return 2;
return 3;
}
可见,这样的判断仅对于单个字符,就避免了繁杂的判断条件。
下一步,我们需要在“in_number”状态下将当前字符计入总数:
int sign = 1; //记录正负
long long ans = 0; //记录总数
void get(char c)
{
state = table[state][get_col(c)];
if(state=="in_number") //在“in_number”状态就需要记录总数
{
ans = ans*10+c-'0';
ans = sign ==1?min(ans,(long long)INT_MAX):min(ans,-(long long)INT_MIN); //此处计算的是ans的绝对值
}
else if(state == "signed") //判断正负
sign = c =='+'?1:-1;
}
我们将上述操作封装在一个名为Automation的类中,此后我们只需要对字符串中的字符进行依次遍历,最后再返回Automation中的ans即可,当然不要忘记考虑正负。
以下是完整代码:
class Automation{
string state = "start";//初始state为“start”
unordered_map<string,vector<string>> table ={
{"start",{"start","signed","in_number","end"}},
{"signed",{"end","end","in_number","end"}},
{"in_number",{"end","end","in_number","end"}},
{"end",{"end","end","end","end"}}
};
//使用unordered_map更高效地查找元素
int get_col(char c){
if(isspace(c))
return 0;
if(c=='+' || c=='-')
return 1;
if(isdigit(c))
return 2;
return 3;
}
public:
int sign = 1; //记录正负
long long ans = 0; //记录总数
void get(char c)
{
state = table[state][get_col(c)];
if(state=="in_number") //在“in_number”状态就需要记录总数
{
ans = ans*10+c-'0';
ans = sign ==1?min(ans,(long long)INT_MAX):min(ans,-(long long)INT_MIN); //此处计算的是ans的绝对值
}
else if(state == "signed") //判断正负
sign = c =='+'?1:-1;
}
};
class solution{
public:
int strToint(string str){
Automation automation;
for(auto x:str)
{
automation.get(x);
}
return automation.sign * automation.ans;
}
};