两个字符串依次比对,遇到匹配失败后,短的回位至0位,长的在原基础上后移一位,继续匹配,直至完全匹配成功,输出首位位置。
【注】
对原基础的理解:
有时,会存在前面几个都匹配成功,但后面出现匹配失败时,记位的变量已经“走了”一段,故应先减去已走部分(也即回到原基础上),再后移一位。
如图所示,当第一次匹配到f时匹配失败,前面成功,因此可以知道长的字符串b,c位置为b,c,此后就无需再用短字符串的a与b,c匹配,直接用a与匹配失败位置的d匹配即可,若直接失败,就后移一位即可,此后类似,直至匹配成功。
此时跳过了c,f,个数为2,也即匹配成功的字符串的最长前后缀的长度(棕色部分地长度,为2)。
通俗的讲,长串与短串依次分别加1,若发生失配,长串不回溯,短串回溯到与失配字符前面子串的最长公共前后缀的长度相同的位置,如图,最长公共子串的长度为2,则短串回溯到2的位置,也即从c开始继续匹配。
(非完整)
int IndexKmpMinus1(char* chang, char* duan){ int* next = genNext(duan); int c = 0,d = 0; while (c < strlen(chang) && d < strlen(duan)){ if(chang[c] == duan[d]){//若匹配 c++; d++; }else{//否则 d = next[d]; } } return d == strlen(duan) ? c - d : -1; }(非完整)
此处分析:若0号位对应的next表的值还是0,当d=0时,进入死循环,d一直被赋值为0,又因为长串位置本身就不变,这种情况下短串位置也不变,则卡死。
解决方法1(就让0号位的next表值为0),代码:
int IndexKmpMinus1(char* chang, char* duan){ int* next = genNext(duan); int c = 0,d = 0; while (c < strlen(chang) && d < strlen(duan)){ if(d == 0 && chang[c] != duan[d])//解决方法 c++; if(chang[c] == duan[d]){//若匹配 c++; d++; }else{//否则 d = next[d]; } } return d == strlen(duan) ? c - d : -1; }解决方法2(改变0号位的next表值为-1,真正的KMP),代码:
int IndexKmpMinus2(char* chang, char* duan){ int* next = genNext(duan); int c = 0,d = 0; while (c < strlen(chang) && d < strlen(duan)){ if(d == -1 || chang[c] == duan[d]){//若匹配 c++; d++; }else{//否则 d = next[d]; } } return d == strlen(duan) ? c - d : -1; }
基本思路为:自己与自己匹配
一个看作长串(c),一个看作短串(d)
若匹配:next[c+1]=next[c]+1=d+1
失配:d=next[d]
代码:
int* genNext (char* duan ){ int* next = (int*)calloc(strlen (duan), sizeof(int)); next[0] = -1; int c = 0, d = -1; while ( c < (int)strlen (duan) - 1){ if (d == -1 || duan[c] == duan[d] ){ c++; d++; next[c] = d; }else d = next[d]; } return next; }一些问题及改进:
这不好!因此如果出现这种情况就跳过去找下一个next表值。
改进代码:
int* genNext (char* duan ){ int* next = (int*)calloc(strlen (duan), sizeof(int)); next[0] = -1; int c = 0, d = -1; while ( c < (int)strlen (duan) - 1){ if (d == -1 || duan[c] == duan[d] ){ c++; d++; next[c] = (duan[c] != duan[d] ? d : next[d]); }else d = next[d]; } return next; }上述过程可以用next表表示为: