对KMP算法的理解

KMP算法实现的功能是给定两个字符串T和P,长度分别为n和m,判断P是否在T中出现,如果出现,则返回出现的位置。

在开始之前,首先要理解前缀和后缀这两个概念,前缀指除了最后一个字符以外,一个字符串的全部头部组合;后缀指除了第一个字符以外,一个字符串的全部尾部组合。所以只有一个字符的字符串是没有前缀后缀的。

如下图,判断P是否在T中出现,假设现在已经匹配到a处才出现不匹配,即a位置之前都匹配,则此时要将P字符串向右移动。

我们可以知道A为P的前缀,B为P的后缀,A的长度等于B的长度。

 

 

 

 

 

因为a处已经不匹配,所以要将P向右移动,传统的做法是将P向右移动一位,但是这样没有很好利用到现有的信息。利用KMP算法之后,我们知道P的前缀A和后缀B的最大公共长度,所以可以通过这个最大公共长度,直接将P向右移动N位,令A和B对齐。

那么KMP是怎么知道前缀和后缀的最大公共长度的呢?关键就在于next数组,下面就来求解next数组。

 

根据数学归纳法,假设我们已经求得next[1],next[2],next[3],......,next[a]的值(next数组的下标从1开始,方便理解),接下来要求next[a+1]。

上面说到我们已经匹配到a处了,那么下一步就要匹配T[a]和P[b],T[a]和P[b]的关系有两种情况:

当T[a] = P[b]时,如下图可以知道就相当于前缀和后缀的最大公共长度增加一个单位,所以next[a+1] = next[a] + 1

 

当T[a] ≠ P[b]时,则只能在j位置之前的字符串进行匹配,所以要将P向右移动(等价于减短最大公共长度,减短最大公共部分)。从下图可以看到,将P向右移动之后,绿色块就是减短之后的最大公共部分,其实绿色块就是灰色块的前缀后缀最大公共部分。令绿色块长度为c,灰色块长度为b,

即c = next[b] = next[next[a]]

从上面的推导我们可以知道:

当T[a] = P[b]时,next[a+1] = next[a] + 1 = b + 1

当T[a] ≠ P[b]时,则只能在b位置之前的字符串进行匹配,此时令c=next[b]=next[next[a]],再接下去进行匹配。如果T[a] = P[c],则next[a+1] = next[c] + 1 = next[next[b]] + 1,如果T[a] ≠ P[c],则继续减短next[next[b]]字符串的长度,继续匹配,直到字符串长度为0为止,因为这个过程是有限的,所以总能算出结果。

 

那么next[1]又是怎么得到的?

如下图,当T[a] ≠ M[b]时,此时M已经不能再向右移动了,所以next[1] = -1

 

posted on 2017-09-08 14:42  evanxwj  阅读(122)  评论(0编辑  收藏  举报