【算法解析】
● 众所周知,KMP算法中模式串T的next数组,是KMP算法的核心。
next数组的核心作用是“当模式串T的第j位与主串S的第pos位失配时(即 T[j]≠S[pos] 时),让模式串T的第next[j]位与主串S的第pos位再进行比较”。这相当于让模式串T往右移动了 j-next[j] 位后,再进行比较。
● 正是由于next数组的引入,使得KMP算法的效率大大优于BF算法。但是,next数组在某些情况下仍存在缺陷。
例如,针对模式串"aaaab",其相应的next数组值为“-1 0 1 2 3”。则据next数组的作用易知,当 T[3]=a 与主串 S[pos] 失配时,则会使用 T[next[3]]=T[2]=a 与主串 S[pos] 再进行匹配,显然还会失配。这是因为,T[0]~T[2]与T[3]都相等,当T[3]与S[pos]失配时,T[0]~T[2]与S[pos]必然也失配。显然T[0]~T[2]与S[pos]的比较没有意义,一定会匹配失败。针对本模式串"aaaab"而言,将会有 T[3]=a≠S[pos],T[next[3]]=T[2]=a≠S[pos],T[next[2]]=T[1]=a≠S[pos],T[next[1]]=T[0]=a≠S[pos] 等四次比较。
● nextval数组的引入,正是为了减少这种无意义的比对,而对next数组进行的优化。
【手动求nextval数组的步骤】
● 手动求nextval数组(模式串下标从1开始)
若在KMP算法设计中,将模式串下标从1开始计数,那么求nextval数组的算法步骤为:
(1)求出next数组的值(定义next[1]=0,next[2]=1);
(2)定义nextval[1]=0。然后,比较模式串的第j个(j>1)字符是否与第next[j]个字符相等。若相等,则模式串第j个字符的nextval值等于第next[j]个字符的nextval值。若不等,则模式串第j个字符的nextval值等于其next数组值。
即,若T[j]=T[next[j]],则nextval[j]=nextval[next[j]]。否则,nextval[j]=next[j]。
● 手动求nextval数组(模式串下标从0开始)
若在KMP算法设计中,将模式串下标从0开始计数,那么求nextval数组的算法步骤为:
(1)求出next数组的值(定义next[0]=-1,next[1]=0);
(2)定义nextval[0]=-1。然后,比较模式串的第j个(j>0)字符是否与第next[j]个字符相等。若相等,则模式串第j个字符的nextval值等于第next[j]个字符的nextval值。若不等,则模式串第j个字符的nextval值等于其next数组值。
即,若T[j]=T[next[j]],则nextval[j]=nextval[next[j]]。否则,nextval[j]=next[j]。
【求nextval数组的算法代码】
#include<iostream>
using namespace std;
const int maxn=100;
int ne[maxn],nev[maxn];
void getNext(string s) {
int len=s.length();
int i=0,j=-1;
ne[0]=-1;
while(i<len) {
if(j==-1||s[i]==s[j]) {
i++;
j++;
ne[i]=j;
} else j=ne[j];
}
}
void getNextval(string s) {
int len=s.length();
int i=0,j=-1;
nev[0]=-1;
while(i<len) {
if(j==-1||s[i]==s[j]) {
i++;
j++;
nev[i]=j;
if(s[i]!=s[j]) nev[i]=j;
else nev[i]=nev[j];
} else j=nev[j];
}
}
int main() {
string T;
getline(cin,T);
getNext(T);
for(int i=0; i<T.length(); i++) {
cout<<ne[i]<<" ";
}
cout<<endl;
getNextval(T);
for(int i=0; i<T.length(); i++) {
cout<<nev[i]<<" ";
}
return 0;
}
/*
input:
abcaabbabcab
output:
-1 0 0 0 1 1 2 0 1 2 3 4
-1 0 0 -1 1 0 2 -1 0 0 -1 4
*/
【编程技巧】
● 由于字符串的下标从0开始,因此采用李春葆《数据结构》中的方式定义nextval[0]=-1,nextval[1]=0是很自然的事情。之后,利用语句 cout<<nextval[i]<<" "; 便可输出以“-1 0 ”开头的nextval数组值。
● 若想输出以“0 1”开头的nextval数组值,只需修改语句cout<<nextval[i]<<" "; 为cout<<nextval[i]+1<<" "; 便可。其他代码保持不变。
【参考文献】
https://blog.csdn.net/qq_43456605/article/details/119954614
https://blog.csdn.net/qq_35963993/article/details/106236665