DFA与NFA的比较

正则表达式的规则很容易理解,但是正则表达式并不能直接用来解析字符串,我们还要引入一种适合转化为计算机程序的模型。今天我们引入的这种模型就叫做有穷自动机(finite automation,FA),有时也叫有穷状态机(finite state machine)。有穷自动机首先包含一个有限状态的集合,还包含了从一个状态到另外一个状态的转换。有穷自动机看上去就像是一个有向图,其中状态是图的节点,而状态转换则是图的边。此外这些状态中还必须有一个初始状态和至少一个接受状态。FA的表达能力等价于正规表达式或者正规文法。

     在使用正则表达式搜索之前,两种引擎都会编译表达式,得到一套内化形式,适应各自的匹配算法。NFA的编译过程通常要快一些,需要的内存也更少一些。对于“正常”情况下的简单文本匹配测试,两种引擎的速度差不多。一般来说,DFA的速度与正则表达式无关,而NFA中两者直接相关。

  DFA不需要做太多的优化,因为它的匹配速度很快,不过最重要的是,DFA在预编译阶段所作的工作提供的优化效果,要好于大多数NFA引擎复杂的优化措施。

现代DFA引擎经常会尝试在匹配需要时再进行预编译,减少所需的时间和内存。因为应用的文本各异,通常情况下大部分的预编译都是白费工夫。因此,如果在匹配过程确实需要的情况下再进行编译,有时候能节省相当的时间和内存(技术术语就是“延迟求值(lazy evaluation)”)。这样,正则表达式、待匹配的文本和匹配速度之间就建立了某种联系。

 

 

NFA:非确定性有限状态自动机

DFA:确定性有限状态自动机

NFA是用正则表达式去匹配字符串,如果匹配到就停止返回, DFA是用字符串去匹配正则表达式,例如 用正则表达式:nfa|nfa not 来匹配字符串 nfa not, NFA是先拿着正则表达式nfa去匹配字符串nfa not,因为正则表达式nfa匹配到了字符串nfa not前面的nfa匹配成功则返回nfa,如果匹配不到再拿着后面整体去匹配,在DFA中是拿着nfa|nfa not整体去匹配字符串nfa,匹配到后面的nfa not.

NFA是表达式主导,DFA是文本主导

表达式主导: 如用正则表达式:to(nite|knite|night) 来匹配文本:tonight,正则表达式中第一个是‘t’,他会重复尝试,直到在匹配文本中找到‘t’为止,之后检查后续文本是否匹配‘o’,如果能,会接着检查选择分支,引擎会依次尝试‘nite’,‘knite’,‘night’这三种可能,直到匹配成功或全部尝试完后匹配失败,在匹配‘nite’时,会上面一样次匹配‘n’,‘i’,‘t’,‘e’,如果失败,在尝试另一种可能。表达式中的控制权在不同的元素之间转换,所以称之为“表达式主导”。

 

文本主导: DFA引擎在扫描字符串时,会记录当前有效的所有可能匹配,如上例中,当扫描到匹配文本的‘i’字符时,它会记录下两个可能匹配的位置‘nite’中的‘i’和‘night’中的‘ni’,从而把knight淘汰,再扫描匹配文本中的‘g’,这时只有‘night’能匹配,只剩下一种可能,然后依次匹配‘h’和‘t’,引擎发现匹配已经完成,报告成功。它是扫描的字符串中的每个字符都对引擎进行了控制,所以称为“文本主导”。

NFA效率会低于DFA,因为DFA每个字符只会扫描一次,而NFA对同一字符会扫描多次。

NFA即假如一个输入符号,可以得到两个或者两个以上可能的状态,也就是说得到的可能的状态是不确定;DFA即假如一个输入符号,只能得到唯一的一个可能的状态。

 

只需要很简单的规则,就能把任何正则表达式转化成NFA,而任何一个NFA又都可以转化为DFA,这样我们就能把正则表达式转化为易于编程的DFA,来真正进行词法分析的工作。

 

下面的图展示了一个有穷自动机,有根从外边来的箭头指向的状态表示初始状态,有个黑圈的状态是接受状态:

 

现在我们来看看有穷自动机怎么处理输入的字符串:

1.一开始,自动机处于初始状态

2.输入字符串的第一个字符,这时自动机会查询当前状态上与输入字符相匹配的边,并沿这条3.边转换到下一个状态。

4.继续输入下一个字符,重复第二步,查询当前状态上的边并进行状态转换

当字符串全部输入后,如果自动机正好处于接受状态上,就说该自动机接受了这一字符串

刚才我们画的自动机,假如输入的字符串是"hello"(带引号)。一开始状态机处于状态1,输入引号以后就沿引号的边转换到了状态2;接下来输入hello都会沿着a-z这条边回到状态2,最后输入引号,转换到了状态3。由于状态3是接受状态,那么这个自动机就会接受这个字符串。而如果字符串是"abc(不带后面的引号),那么当字符串输入完毕之后自动机会处在状态2,而状态2不是接受状态,所以这个自动机就不接受"abc这个字符串。一个自动机接受的所有字符串组成的集合称作这个自动机的语言。这里语言的概念和上一回我们介绍正则表达式的语言概念是一样,都表示一个有限字符集上的字符串集合。

 

上面我们画的自动机是一个确定性有穷自动机(DFA),其特点是从每一个状态只能发出一条具有某个符号的边。也就是说不能出现同一个符号出现在同一状态发出的两条边上。但是,还有一种非确定性有穷自动机(NFA),它允许从一个状态发出多条具有相同符号的边,甚至允许发出标有ε(表示空)符号的边,也就是说,NFA可以不输入任何字符就自动沿ε边转换到下一个状态。

下图展示了一个非确定性有穷自动机:

DFA与NFA匹配结果的差异:

DFA(或者POSIX NFA)返回最左边的最长的匹配文本。传统型NFA可能返回同样的结果,当然也可能是别的文本。针对某一型具体的引擎,同样的正则表达式,同样的文本,总是得到同样的结果,在这个意义上来说,它不是“随机”的,但是其他NFA引擎可能返回不一样的结果。

DFA不能支持捕获括号和反向引用。

在NFA中一个节点对应的下一个状态节点会有多个,但是在DFA中只有唯一的节点与之对应。NFA转DFA的过程是将NFA一个节点对应的下一个所有节点的集合当做DFA的一个节点来处理。可以利用子集构造法解决。

 

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值