统计单词个数———有穷状态机编程

有穷状态机的形式定义

有穷状态机是一个五元组 (Q,Σ,δ,q0,F),其中:
Q是一个有穷集合,称为状态集。
Σ是一个有穷集合,称为字母表。
δ: Q xΣQ称为状态转移函数。
q0 是初始状态。
F 是接受状态集。

教科书上是这样定义有穷自动机的,这个形式定义精确的描述了有穷状态机的含义。但是大部分人(包括我自己)第一次看到它时,反复的读上几遍,仍然不知道它在说什么。幸好通过一些实例,我们可以很容易明白有穷状态机的原理。

自动门是一个典型的有穷状态机:

它有“开”和“关”两种状态,这就是它的状态集,也就是上面所说的Q。

人可以从自动门进来或出去,当人进来或出去的时候,自动门会自动打开,如果在规定的时间内没有人进出,自动门会自动关上。人的进来、出去和超时三个 事件是自动门的字母表,也就是上面所说的Σ。而自动门在当前状态下,对事件的响应,会引起状态的变化,这就是状态转换函数,也就是上面所说的δ。

自动门刚安装好的时候,我们可以认为它是关上的,所以关闭状态是自动门的初始状态。

在理想情况下,自动门会一直运行,所以它没有接受状态,接受状态集F是空集。

有穷状态机的形式定义很精确,文字描述比较通俗,而图形表示则比较直观。通用建模语言(UML)里的状态图是状态机的常用图形表示方法。简单的状态 图包括一些状态,用圆角方框表示,里面有状态的名称。状态之间的转换,用箭头表示,上面可以加转换条件。自动门的状态机可以用下图表示:

有穷状态机很简单,在生活中可以找出很多这样的例子。但是教科书里讲得太复杂了,一会儿证明确定性有穷状态机和非确定性有穷状态机的等价性,一会儿 证明正则表达式的正则运算是封闭的,一会儿又来个泵引理。花了很长时间,我才明白这些原理,但两年之后,我又把它们忘得一干二净。

主要原因是工作中没有机会运用它们,这些理论的证明于编程没有太大用处,不过状态机本身却是文本处理利器,由于程序员在很多场合下都是在与文本数据 打交道,所以状态机是程序员必备的工具之一。

统计一篇英文文章里的单词个数。
有多种方法可以解这道题,这里我们选择用有穷状态机来解,做法如下:
先把这篇英文文章读入到一个缓冲区里,让一个指针从缓冲区的头部一直移到缓冲区的尾部,
指针会处于两种状态:“单词内”或“单词外”,加上后面提到的初始状态和接受状态,就
是有穷状态机的状态集。缓冲区中的字符集合就是有穷状态机的字母表。
如果当前状态为“单词内”,移到指针时,指针指向的字符是非单词字符(如标点和空格),
那状态会从“单词内”转换到“单词外”。如果当前状态为“单 词外”, 移到指针时,指
针指向的字符是单词字符(如字母),那状态会从“单词外”转换到“单词内”。这些转换规
则就是状态转换函数。
指针指向缓冲区的头部时是初始状态。
指针指向缓冲区的尾部时是接受状态。
每次当状态从“单词内”转换到“单词外”时,单词计数增加一。
这个有穷状态机的图形表示如下:


代码如下:

#include<iostream>
using namespace std;

bool Is_Word(char ch)
{
	if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
		return true;
	return false;
}

int Calc_Words(const char* text)
{
	enum State
	{
		WORD_INIT,  //初始状态
		WORD_IN,    //单词中
		WORD_OUT    //单词外
	}state = WORD_INIT;
	
	int count = 0;
	const char *p = text;

	for(p = text; *p != '\0'; p++)
	{
		switch(state)
		{
		case WORD_INIT:
			{
				if(Is_Word(*p))
				{
					state = WORD_IN;
				}
				else
				{
					state = WORD_OUT;
				}
			}
			break;
		case WORD_IN:
			{
				if(!Is_Word(*p))
				{
					count++;
					state = WORD_OUT;
				}
				break;
			}
		case WORD_OUT:
			{
				if(Is_Word(*p))
				{
					state = WORD_IN;
				}
				break;
			}
		default:
			break;
		}
	}

	if(state == WORD_IN)
	{
		/*如果由单词内进入接受状态,增加单词计数*/
		count++;
	}

	return count;
}

int main()
{
	char *text = "Where there is a will,there is a way!All the things in their being are good for something!";
	int count  = 0;
	count  = Calc_Words(text);
	cout<<"There are "<<count<<" words in the text totaly!"<<endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值