字符串匹配——KMP算法

  上一节的利用有限自动机的匹配算法需要O(m|∑|)的预处理时间,因为预先已经把转移函数计算出来。而KMP算法则是将计算转移函数时需要的信息存储在数组π[1..m]中,按照即时的需要计算状态转移函数,这样可以把预处理时间减少到O(m),而匹配时间仍然是O(n)。

 

Knuth-Morris-Pratt 算法

  首先简单看一下朴素字符串匹配算法的过程。假设模式P = ababaca,图(a)是针对文本T模板的一个特定偏移s。

  此时前5个字符匹配成功,第6个失败,这时已经匹配的前5个字符其实已经蕴含了下一个偏移可以利用的一些信息。如图,偏移s + 1必然无效,因为模式P前2个字符不等而第2个字符与T匹配。

  如图(b),偏移s' = s + 2时,由于刚才前5个匹配的字符中前3个等于后3个,所以模式P向右移动2位后显然前3位于T仍然是匹配的,这样可以直接从P的第4位开始检验。

 

  现在假设模式字符P[1..q]与文本字符T[s + 1..s + q]匹配,s'是最小的偏移量,s'  > s。如果P[q + 1] = T[s + q + 1]则匹配数加1,即q = q + 1.关键是下一位不匹配时该怎么处理。此时我们希望能找到P[1..q]的一个最长真前缀P[1..k],同时也是T[s + 1..s + q]的后缀,这样就可以从P的k + 1位继续比较了。容易发现T[s + 1..s + q]

的后缀也就是P[1..q]的后缀,因此我们在预处理时只需要找出k并存在π[q]中即可。

  尝试计算π[q],并假设π[q - 1] = k已经计算出来,就是说P[1...q - 1]的最长前缀同时也是后缀为P[1..k]。如果P[q] = P[k + 1],容易得出π[q] = k + 1.如果不等,这时只能寻找P[1..q - 1]中比k更短的最长前缀同时也是后缀的P[1..k'],而这时k'只能是π[k]了。如果P[q] = P[k' + 1]就可以得出π[q] = k' + 1,否则就要寻找比k'更短的k''......

下面是预处理的伪代码:

 

有了前缀函数后就可以进行匹配:

 

  关于KMP算法的正确性的证明比算法的实现稍微复杂,可以参考《算法导论》。

 

posted @ 2013-11-29 08:51  Jolin123  阅读(451)  评论(0编辑  收藏  举报