- 设主串为T,匹配串为P
KMP算法的优点是通过证明了已匹配模式串子串的最大前后缀来保证模式串指针j可以不用回到初始位置,算法时间复杂度从O(lenT*lenP)简化成了O(lenT+lenP)
- 先从朴素算法开始,从T[0],P[0]扫描,可以发现第6个字符不匹配
- 设j是T串失配处的指针,也等于第一次失配成功部分的子串长度,下面要从该子串中找到更有效的部分。
考虑j的下一个位置:我们想要的是将j尽量往后移动,因为TGTG!=GTGT,GTG==GTG,TG!=GT,
第一次和第二次的公共串不相同,也就是第二次和T串的公共部分不相同,可以跳过这次匹配;而第三次和第一次的公共串相同,都是GTG,也就是第三次和T串的公共部分相同,
由于公共项都已经匹配成功,直接让j移动到GTG后的字符位置,而T串的指针i不用动,还是A这个位置。
所以大可不必进行朴素算法匹配,这样就得到了KMP算法
T:G T G T G
P:G T G T G 1
P: G T G T G 2
P: G T G T G 3
P: G T G T G 4
...
我们的比较是通过让第k次BF算法移动的模式串和第1次模式串公共部分进行比较从而间接让第k次的模式串和T串的公共部分进行比较,从而得到最大匹配子串,也就是最大前后缀子串,最后得到next[j]的值。
-
通过观察可以看到已匹配串最大前后缀是GTG
将j移动到公共部分之后的位置,i停留在失配的位置,继续匹配
-
按照上面的查找最长前后缀跳转j的规律匹配下去,最终匹配成功
-
next[j],篇幅长,回头再说
要让机器去寻找j的下一个位置并非容易的事,next[]是一个一维整型数组,数组的下标代表了“已匹配前缀的下一个位置”,元素的值则是“最长可匹配前缀子串的下一个位置”。
规定 -
next[0]和next[1]的值是0,因为这时候不存在前缀子串,因此k=0
-
如果P[j]==P[k],next[j+1]=next[j]+1=k+1
-
如果P[j]!=P[k],next[j+1]=next[k]+1=next[next[j]]+1