DFA运行时间不是O(n),而是O(nm)

时间:2016-08-16 16:13:58

标签: algorithm big-o dfa nfa

如果你有一个包含m个节点的DFA并且你在一个包含n个字符的字符串上运行它,那么在每一步你不仅要测试从前一步继承的状态,还要再次测试DFA的第一个状态。因此,在字符串中的m个字符(假设m< n)之后,最坏的情况是你有mn个活动状态要测试(每个状态只需要一个查找来提前或被解雇)

例如,考虑{l} b正则表达式(所有单词以重复l次开头,后跟b),其DFA具有m = l + 1个节点。将它与字符串a {k} b匹配,其中k> l表示您将遇到字符串中l个字符后具有(m - 1)个活动状态的最坏情况。

我错过了什么?或者,文献中的实际实现是否仅仅涉及了解一个给定的完整字符串(即不是其中一个子字符串)是否与正则表达式匹配的理论问题。

从我所在的地方开始运行NFA或DFA需要O(nm)次(m是NFA或DFA中的节点数和n个字符数)。唯一的问题是NFA比DFA拥有更多节点。

2 个答案:

答案 0 :(得分:1)

历史上,DFA首先被定义为匹配整个字符串而不是搜索子字符串,这就是为什么文献通常会讨论DFA的时间复杂度以获取单个字符串然后返回整个字符串是否匹配或不。如果您有一个与整个字符串匹配的DFA并且您想用它来搜索子字符串,那么您实际上是多次运行DFA,每个可能的起始位置运行一次,这就是为什么你得到O(mn)作为你的运行时而不是O(n)。

但是,如果您的目标是在某处匹配子字符串,那么您可能最好重新设计DFA。想象一下,例如,您想要使用DFA匹配一些正则表达式R.不是为R构建DFA并从每个可能的位置开始运行它,而是为正则表达式Σ*RΣ*构建DFA。现在,如果输入的任何子串与R匹配,则整个字符串与Σ*RΣ*匹配,因此您只需要在字符串上运行DFA的单次传递。这会将运行时降低到O(n),因为您只运行一次传递。

答案 1 :(得分:0)

如果您确实拥有DFA,则不会有多个活动状态。 DFA定义为只有一个活动状态。每个角色只能导致下一个状态。

如果您使用此属性,则从开始状态开始并消耗n个字符。在每个角色你检查: - 如果没有转换到非错误状态=>不匹配 - 如果转换到非错误状态=>继续

最后检查您当前的状态是否为最终状态。如果是这样=>成功,否则=>不匹配。

从我的观点来看,NFA需要O(n * m),其中DFA取O(n)。 DFA性能不依赖于模式复杂性(节点数)。

但我不知道你为什么接受一个带有DFA而不是DFA字符串匹配的字符串搜索(实际上不是O(n))的答案。但是,如果这是你的问题:有些算法是从DFA派生的,比搜索Σ做得更好,这将是Knuth-Morris-Pratt(对于单一模式)和Aho- Corasick(多种模式)。底层DFA是压缩的,但两种算法共享属性,它们只对一个不具有多个状态的字符进行一次转换(如在NFA中)。