KMP算法详解附c代码
KMP详解:
目标:找到目标串当中,和模板串相同的子串位置
我们考虑a为模板串,b为目标串,设置i为目标串的索引,j为模板串的索引
1.暴力(BF算法)做法:i逐个+1,每个位置分别和模板串比对,看是否对。复杂度:i*j
改善暴力做法思路:可以注意到暴力做法里面某一个i的比对,可能是前面k个字符一样,但是第k+1个字符不一样,我们可以利用这对比过的k个字符,不让j减回0,从而减少对比次数,这就是KMP
2.KMP:我们假设在i和j位置后面停住,意味a[i]==b[j],a[i+1]!=b[j+1]。如果是暴力做法,我们就要让i->i+1,j->0,但是如果我们能找到a的最长前后公共子串,如:abcvfsdabcv|(此次比对不一,停住)ad.... ,在下一次,我们可以让索引j从前面的abcv的末尾查找,相当于第i+1次的的开头abcv和第i次的末尾abcv重合上了
而对于模板串a的每个子串,他的最长的前后公共串是一定的,所以我们定义一个next数组,满足:某个长度(对应KMP比对中停住,已经比对了的模板串长度)j,next[j]=k,这里k是最长前后公共子串的长度。下一次j从k开始
next求法:next[1]=0,next[2]开始比对,后面的按两种逻辑
1.如果a[k+1]==a[j+1],由于前面的子串都相等,而增加的a[j+1]也和a[k+1]对的上,所以next[j+1]=k+1;
2.如果1不成立,我们需要重新找next[k+1]。注意到这种情况next[j+1]不可能大于next[j],因为如果大于了,代表最长前后公共子串等于k+1,也就是增加的a[j+1]也和a[k+1]对的上,和1不成立矛盾!于是,该子串一定存在于0~k中,同时注意到,如果假设next[j+1]=t,那么前t-1个数刚好能和 新加进来a[j+1]之前的 倒数t-1个值合的上(next[j]==k,而t<k)
也就是说,我们计算next[j+1],只需令k=next[k],然后比对a[k+1]和a[j+1],对的上,结束,对不上,继续回溯k=next[k],直到遇到k[1]=-1,这时令next[j+1]=0;结束
程序思路:
1.kmp:定义指针i,j,在while循环内,如果a[i]==b[j],说明匹配上,i++,j++。匹配上之后,不断比对,直到遇到不同,这时回退。直到j到达长度N。我们设计next[0]=-1;,等于-1说明没配上,i++,j++。
2.getnext:非常类似kmp函数,我们把a[j]看成b[j]就很好理解了。初始化next[0]=-1;让j=-1时表示开头,i++,j++
代码里面j=-1代表走投无路,匹配不上,只能老实+1,
事实上也可以令j=0,i=1,next[1]=0;遇到零就回溯。这种写法舍弃了数组的第零个值。
然后就是错位匹配,对应某长度下的next数值。
#include<stdio.h>
#include<string.h>
const int N=1000,M=10000;
void getnext(char *a,int *next);
int kmp(char *a,char *b,int *next);//a 模板串,b目标串
int main(){
char a[N],b[M];
int next[N];
scanf("%s %s",a,b);
getnext(a,next);
printf("%d",kmp(a,b,next));
}
int kmp(char *a,char *b,int *next)
{
int i=0,j=0;//j-a,i-b
int len1=strlen(a);
int len2=strlen(b);
while(i<len2){
if(j==-1||a[j]==b[i]){
i++;
j++;
}
else{
j=next[j];
}
if(j==len1)return i-len1;
}
return -1;
}
void getnext(char *a,int *next){
int len=0;
int i=0,j=-1;
next[0]=-1;
len=strlen(a);
while(i<len)
{
if(j==-1||a[i]==a[j]){
i++;
j++;
next[i]=j;
}
else{
j=next[j];
}
}
}

浙公网安备 33010602011771号