KMP算法(Knuth-Morris-Pratt算法)是一种在文本字符串中高效查找子串(模式串)出现位置的算法。它避免了最简单的暴力匹配方法中的大量回溯,通过构建一个next数组(或者称为“部分匹配表”)来指导匹配过程。
我看了网上很多人的方法,感觉都没有把next数组该如何求解的问题说明白。因此,我在仔细钻研了王道还有数据结构等教材后,总结出了下面的求解方法。
Next数组构建方法:
公式:next[j]=前后缀最长公共子序列长度+1
规定:第一个和第二个元素的next值分别为0、1
例题1
看下图的例子,求解子串【abcac】的next数组:
第一步:
由规定前两个值分别为0、1,我们直接看第三个字符【c】,字符【c】之前的串为【ab】
前缀:【a】
后缀:【b】
没有最长公共子序列,因此前后缀最长公共子序列长度:0
next[3]=0+1=1
第二步:
我们看第四个字符【a】,字符【a】之前的串为【abc】
前缀:【a】,【ab】
后缀:【c】,【bc】
没有最长公共子序列,因此前后缀最长公共子序列长度:0
next[4]=0+1=1
第三步:
我们看第五个字符【c】,字符【c】之前的串为【abca】
前缀:【a】,【ab】,【abc】
后缀:【a】,【ca】,【bca】
存在最长公共子序列【a】,因此前后缀最长公共子序列长度:1
next[5]=1+1=2
例题2
看下图的例子,求解子串【abaabcaba】的next数组:
第一步:
由规定前两个值分别为0、1,我们直接看第三个字符【a】,字符【a】之前的串为【ab】
前缀:【a】
后缀:【b】
没有最长公共子序列,因此前后缀最长公共子序列长度:0
next[3]=0+1=1
第二步:
我们看第四个字符【a】,字符【a】之前的串为【aba】
前缀:【a】,【ab】
后缀:【a】,【ba】
存在最长公共子序列【a】,因此前后缀最长公共子序列长度:1
next[4]=1+1=2
第三步:
我们看第五个字符【b】,字符【b】之前的串为【abaa】
前缀:【a】,【ab】,【aba】
后缀:【a】,【aa】,【baa】
存在最长公共子序列【a】,因此前后缀最长公共子序列长度:1
next[5]=1+1=2
第四步:
我们看第六个字符【c】,字符【c】之前的串为【abaab】
前缀:【a】,【ab】,【aba】,【abaa】
后缀:【b】,【ab】,【aab】,【baab】
存在最长公共子序列【ab】,因此前后缀最长公共子序列长度:2
next[6]=2+1=3
第五步:
我们看第七个字符【a】,字符【a】之前的串为【abaabc】
前缀:【a】,【ab】,【aba】,【abaa】,【abaab】
后缀:【c】,【bc】,【abc】,【aabc】,【baabc】
没有最长公共子序列,因此前后缀最长公共子序列长度:0
next[7]=0+1=1
第六步:
我们看第八个字符【b】,字符【b】之前的串为【abaabca】
前缀:【a】,【ab】,【aba】,【abaa】,【abaab】,【abaabc】
后缀:【a】,【ca】,【bca】,【abca】,【aabca】,【baabca】
存在最长公共子序列【a】,因此前后缀最长公共子序列长度:1
next[8]=1+1=2
第七步:
我们看第九个字符【a】,字符【a】之前的串为【abaabcab】
前缀:【a】,【ab】,【aba】,【abaa】,【abaab】,【abaabc】,【abaabca】
后缀:【b】,【ab】,【cab】,【bcab】,【abcab】,【aabcab】,【baabcab】
存在最长公共子序列【ab】,因此前后缀最长公共子序列长度:2
next[9]=2+1=3
KMP算法匹配:
公式:右移位数=已匹配字符数-最后一个匹配字符next值
规则:当子串与主串的字符发生失配时,子串右移,指针不变动,然后从指针指向主串当前位置开始重新匹配
例题1
【2015统考真题】已知字符串S为'abaabaabacacaabaabcc',模式串t为'abaabc'。采用KMP算法进行匹配,第一次出现“失配”(s[i]≠t[j])时,i=j=5,则下次开始匹配时,i和j的值分别是()。
A.i=1,j=0 B.i=5,j=0 C.i=5,j=2 D.i=6,j=2
解答:
先计算模式串t的next数组,如图所示
第一次进行匹配,子串第6个字符【c】与主串S第6个字符【a】失配,此时i=j=5,说明主串和模式串的位序从0开始排列,子串右移,进行下一次匹配
目前已匹配字符数为5
子串最后一个匹配字符为【b】,其next值为2
因此,右移位数=5-2=3
指针不变动,此时从主串第6个字符和子串第3个字符开始匹配,即从主串s[5]和子串t[2]开始,因此,i=5,j=2,答案选C
例题2
【2019统考真题】设主串T='abaabaabcabaabc',模式串S='abaabc'。采用KMP算法进行模式匹配,到匹配成功时为止,在匹配过程中进行的单个字符间的比较次数是()。
A.9 B.10 C.12 D.15
解答:
先计算模式串S的next数组,如图所示
第一次进行匹配,子串第6个字符【c】与主串T第6个字符【a】失配,子串右移,进行下一次匹配,第一次匹配字符一共比较了6次
目前已匹配字符数为5
子串最后一个匹配字符为【b】,其next值为2
因此,右移位数=5-2=3
第二次进行匹配,指针不变动,此时从主串第6个字符和子串第3个字符开始匹配,匹配成功,第二次匹配字符一共比较了4次
因此两次匹配字符总共比较了6+4=10次,答案选B