为什么KMP算法与主串无关,只与模式串有关:
因为模式串是与主串匹配的,模式串才是关键,主串要么就没有与模式串匹配的字串,要么就必然有字串与模式串匹配,所以说最终还是回到了模式串。而实际上KMP算法的关键就是模式窜找自己的字串,然后用next数组记录下来,用next数组来控制匹配时模式串滑动的长度。
怎样求next数组呢?
j | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
模式串 | a | b | c | a | a | b | a | b | c |
next[j] | 0 | 1 | 1 | 1 | 2 | 2 | 3 | 2 | 3 |
next[1]=0是固定的。
next[2]=1,则模式串长度为2=“ab”,这时ab的最大字串长度为1,无法在字串中找到与之匹配的字串,故next[2]=1,表示下次若与主串匹配时若在2位置失配,就得回溯到1位置重新匹配。
以此类推,则当到next[7]时,next[7]=3,则模式串长度为5=“abcaaba”,这时abcaaba的最大字串长度为4=“abcaab”,这时,前缀ab=后缀ab,个数为2,所以next[7]=2+1=3。表示下次若与主串匹配时若在7位置失配,就得回溯到3位置重新匹配。为什么从3开始匹配呢?因为我们在前面就已经比较过模式串的字串了,知道1、2位置与5、6位置字母相同,所以就没有必要再比较一次了。所以便从第3个位置与失配的位置开始比较。
求next数组的代码:
Void get_next(char t[],int Next[]){
Next[1]=0;
int j=0;//前缀
int i=1;//后缀
while(i<t[0]){
if(j==0||t[j]==t[i]){
//j==0则表示在第一个位置就失配了
i++;j++;
Next[i]=j;
}
else{
j=next[j];
}
}
}
KMP算法代码:
int KMP(int pos,int Next[],char s[],char t[]){
//将主串,模式串,next数组,还有pos(从主串第几个位置开始模式匹配)传过来
int i=pos;j=1;
while(i<=s[0]&&j<=t[0]){
if(j==0||s[i]==t[j]){
//j==0则表示在第一个位置就失配了,则这时主串与模式串就都需要后移一个位置再进行匹配。
i++;
j++;
}
else{
j=Next[j];//右滑
}
}
if(j>t[0]){
Return i-t[0];//返回匹配成功的初始位置
}
else{
return 0;//匹配失败
}
}
其实KMP算法实际上就是对BF算法最耗时的回溯进行了改进,让匹配效率大大提高了。那下面也附上BF算法的代码吧。
int BF(char s[],char t[]){
//将主串,模式串传过来
int i=1;j=1;
while(i<=s[0]&&j<=t[0]){
if([i]==t[j]){
i++;
j++;
}
else{//回溯
i=i-j+2;//返回到主串中第一个没有匹配过的位置
j=1;//返回到模式串中第一个位置与主串重新匹配
}
}
if(j>t[0]){
return i-t[0];//返回匹配成功的初始位置
}
else{
return 0;//匹配失败
}
}