算法总结: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换行符
startstartprocessafter
processprocessprocessafter
afterstartprocessafter

接下来编程部分就非常简单了:我们只需要把上面这个状态转换表写成代码即可,自动机编程的题解如下:

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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

博客著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 8
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值