KMP 算法中对 next 数组的理解
借鉴自大佬:KMP 算法中的 next 数组推导(图解 + 代码实现)_next数组-优快云博客
next 数组的意义
此处 next[j] = k
;则有 k 前面的浅蓝色区域和 j 前面的浅蓝色区域相同;
next[j]
表示当位置 j 的字符串与主串不匹配时,下一个需要和主串比较的字串位置在 next[j]
处;有下图:
若当前位置 j 与主串某一个字符不匹配,则下一次比较的是 K 与主串的当前位置,这个 K 也就是 next[j]
;由于两个浅蓝色区域相同,因此 K 前面的区域肯定与主串相同,不需比较;如下图:
由上图可知,K 前面的区域不需比较;
所以我们next的作用就是在碰到主串与子串不匹配的情况(这里把 0 ~ j-1 看做主串,k~j-1 看做子串)下主串的 i 不动,子串的 j 返回到对应的next位置。然后不断递归直到 j 到了第一个位置或者该位置与 i 匹配。这样可以避免多余的匹配提高代码的效率。
kmp算法的思路和next大同小异就是遍历匹配主串与子串遇到不匹配的情况就调用next。当j==子串的长度的时候说明匹配成功了。如果 i 超出了主串的长度说明没有找到匹配的子串。
源码
注释全写在代码里了
#include<stdio.h>
#include<string.h>
void getnext(char a[],int next[])
{
int n=strlen(a);
next[0]=-1;
next[1]=0;
int t=0; //t为当前索引的前一位的最大相同前后缀的长度及应该回溯的位置(该位置的值对比当前索引的前一位的值)
for(int i=2;i<n;i++){
t=next[i-1]; //每次先给t赋值为前一位的next值
while(t>=0&&a[i-1]!=a[t]){
//当a[i-1]的值与前一位对应的next值不相同时在去找前面的next值直到
//找到第一位(t==-1)及没有最大相同前后缀(该位置的next值就应该是0)
//或者有一个next的值与a[i-1]相等时退出循环(该位置的next值就应该是当前回溯找到的next(t)+1)
t=next[t];
}
if(a[i-1]==a[t] || t==-1){
next[i]=t+1;
}
}
}
int kmp(char s[],char t[]) //s为主串,t是模式串
{
int ls=strlen(s);
int lt=strlen(t);
int next[10000];
getnext(s,next);
int i=0,j=0;
for(;i<ls;i++){ //外层是遍历主串
while(j>0&&s[i]!=t[j]){ //如果匹配不成功主串的i不动j回溯到next(最大相同前后缀)
printf("%d %d\n",i,j);
printf("%c %c\n",s[i],t[j]);
j=next[j];
}
if(s[i]==t[j]) //如果主串与模式串匹配成功i,j后移
{
printf("%d %d\n",i,j);
printf("%c %c\n",s[i],t[j]);
j++;
}
if(j==lt) //如果子串遍历到了最后一位搜索结束返回值
{
return i-j+1;
}
}
return -1; //匹配不成功
}
int main()
{
char s[10000],t[10000];
gets(s);
gets(t);
int next[10000];
getnext(t,next);
for(int i=0;i<strlen(t);i++){
printf("%d ",next[i]);
}
printf("\n");
int x=kmp(s,t);
printf(" %d \n",x);
return 0;
}