算法学习笔记(九)有限状态机 FSM 的应用

一个问题:Beautiful String

这是2014微软校招的编程题,题意大致如下:
如果一个字符串包括三组或者更多组的连续升序字母,每组长度相等,那么我们就称这个字符串是Beautiful String

  • 符合Beautiful String举例:abc, cde, aabbcc, aaabbbccc
  • 不符Beautiful String举例:abd,cba,aabbc,zab
    输入一个只含有小写字母的字符串,如果它含有一个Beautiful的子串,就输出YES,否则输出NO
  • 输入: 第一行是案例个数,之后的每一行是一个数字,一个字符串,数字表示字符串长度,长度小于10MB
  • 输出:YES 或 NO

有限状态机 FSM 定义

有限状态机(Finite-state machine)又称有限状态自动机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。常用与:正则表达式引擎,编译器的词法和语法分析,游戏设计,网络协议,企业应用中等方面。

有限状态机 FSM 要素

状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。

“现态”和“条件”是因,“动作”和“次态”是果。
1. 现态:是指当前所处的状态。
2. 条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
3. 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态 4. 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

有限状态机 FSM 实现

  • 用switch/case 或 if/else 实现,简单粗暴,适合简单的小型状态机;
  • 用设计模式中的 state pattern,把复杂判断的逻辑简化,利于组织代码;
  • 用状态表设计,建立状态表和动作查询表,根据状态表、事件、动作表定位相应的动作处理函数,执行完成后再进行状态的切换;

Beautiful String问题的解法

分析

这个自动机规模小,直接用if/else简单的实现即可。每个状包含4个要素:

  • 当前处理的字符 current
  • 当前处理的字符数量 num_current
  • Beautiful String中上一个字符的数量 num_prev
  • 当前元素是Beautiful String中第几个元素 pos_beauty 

代码

#include<iostream>
using namespace std;

struct states {
	char current;      //  正在处理的字符
	int num_prev;      //  beautiful string中上一个元素的数量
	int num_current;   //  正在处理的字符累计的数量
	int pos_beauty;    //  正在处理的字符是 beautiful string中的第几个元素
} s = { 0, 0, 0, 0 };

int main() {
	int ncase, n;
	char c;
	cin >> ncase;
	while (ncase--) {
		cin >> n;
		bool result = false;
		while (n--) {
			cin >> c;
			if (result) {
				continue;
			}
			if (s.current == 0) {
				s.current = c;
				s.num_current = 1;
				s.pos_beauty = 1;
				continue;
			}
			if (s.current == c) {
				s.num_current++;
				if (s.num_prev != 0 && s.num_current > s.num_prev) {
					s.num_prev = 0;
					s.pos_beauty = 1;
				}
			}
			if (s.current == c - 1) {
				if (s.num_prev == 0 || s.num_current <= s.num_prev) {
					s.pos_beauty++;
				} else {
					s.pos_beauty = 2;
				}
				s.num_prev = s.num_current;
				s.num_current = 1;
			}
			if (s.current != c && s.current != c - 1) {
				s.pos_beauty = 1;
				s.num_current = 0;
				s.num_current = 1;
			}
			if (s.pos_beauty >= 3 && s.num_current == s.num_prev) {
				result = true;
			}
			s.current = c;
		}
		if (result) {
			cout << "YES" << endl;
		} else {
			cout << "NO" << endl;
		}
	}
	return 0;
}

/**  用例:
7
3
abc
4
aaab
6
abccde
3
abb
8
aaaabbcc
11
aaaabbbccde
6
aaabbc
 */

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。