一、算法解析
这是2022版王道书数据结构第110页上的KMP算法求解next数组算法,书中没有详细说明原理,且咸鱼学长也说这是整个课程中比较晦涩的算法之一,引发了笔者的思考。
void get_next(String T,int next[]){
int i=1, j=0;
next[1]=0;
while(i<T.length){
if(j==0||ch[i]==T.ch[j]){
++i; ++j;
next[i]=j; //若pi=pj,则next[j+1]=next[j]+1
}
else
j=next[j]; //否则令j=next[j],循环继续
}
}
求解原理:
1、先检查待求字符 Z 之前 Y 字符的最大匹配长度为 m 个字符,对应为起始的 T[1]-T[m] ,此时若 X(T[m+1])==Y ,则可以判断 Z 之前的最大匹配字符为 m+1 个,故next为 m+2;
2、若 X!=Y ,则需要查找一个①串与②串相匹配,同时由于 X 前的最大匹配字串为 n 个,故②串==③串,同时①串==③串,找到与①串相匹配的②串,此时若 ξ==Y,则 Z 的 next[Z]=n+1;
对应上述代码中的while循环
while(i<T.length){
if(j==0||ch[i]==T.ch[j]){
++i; ++j;
next[i]=j; //若pi=pj,则next[j+1]=next[j]+1
}
else
j=next[j] //否则令j=next[j],循环继续
}
二、KMP模板
模板中 i=0 与 j=-1 是因为程序中字符串是从下标0开始的,而王道讲解是默认第一个字符下标为1
void get_next(string T, int next[]) {
memset(next, 0, sizeof(next)); //多组输入时,每次需要初始化next[]数组
int i = 0, j = -1;
next[0] = -1;
while (i < T.length()){
if (j == -1 || T[i] == T[j]) {
++i; ++j;
next[i] = j; //若pi=pj,则next[j+1]=next[j]+1
}
else
j = next[j]; //否则令j=next[j],循环继续
}
}
int Index_KMP(string S, string T, int next[]) //S是字符串,T是模式串
{
int i = 0, j = 0;
while (i < S.length() && j < T.length()){
if (j == -1 || S[i] == T[j]){
++i; ++j;
}
else
j = next[j];
if (j == T.length()) //匹配成功
return i - T.length();
else //匹配失败
return 0;
}
}
要注意的是,Index_KMP()函数是针对求得模式串匹配索引的函数,若题意要求求字符串内匹配子串的个数时,while循环中的 j < T.length() 需要删去,其中的内容要根据实际情况调整。
另外,get_next()函数可以优化成get_nextval()函数,可以加快字符串的匹配速度,算法细节见2022版王道教材 P111页 4.2.3KMP算法的进一步优化
void get_nextval(string T, int nextval[]) {
memset(next, 0, sizeof(next)); //多组输入时,每次需要初始化next[]数组
int i = 0, j = -1;
nextval[0] = -1;
while (i < T.length()) {
if (j == -1 || T[i] == T[j]) {
++i; ++j;
if (T[i] != T[j]) nextval[i] = j;
else nextval[i] = nextval[j];
}
else
j = nextval[j];
}
}