作用:处理字符串匹配的问题,比正常遍历要快,时间复杂度为O(n)
KMP的大致思路
在比较俩个字符串时,把一个当做模板串,并求出其next数组,在另一个字符串上进行遍历比较,若不同时可以利用next数组移动模板串,因为不需要每次从头比较模板出所有时间复杂度比直接遍历要快;
前缀表
a a b a a b a a f
a a b a a f
前缀 :{a}, {a,a}, {a,a,b} ,{a,a,b,a} , {a,a,b,a,a } 即不包含尾部的所有子串 {a,a,b,a,a,f} 不是因为他包含了尾部
后缀:{f}, {a,f}, {a,a,f}, {b,a,a,f}, {a,b,a,a,f}
即不包含头部的所有子串 {a,a,b,a,a,f} 不是因为他包含了头部
核心:求最长前后缀,
a 0;
aa 1;
aab 0;
aaba 1;
aabaa 2;
aabaaf 0;
得到序列 0 1 0 1 2 0;这个就是前缀表
匹配过程
a a b a a b a a f
a a b a a f
0 1 0 1 2 0
在遍历到b即f对应位置时发现不一致,在此时求前一位的最长相等前后缀 在此样例为2 ,此时移动子串下标为2的元素到f位置(这个2为前面的前一位的最长相等前后缀)
next 数组(遇到冲突时由next数组判断移动位置)
next数组核心为前缀表,不同的数组可能会对前缀表进行不同操作,如整体后移或者整体减一,目的为了进一步优化时间复杂度
代码实现
void get_next(char *a,int*next)//a表示模板串,next表示要求的数组
{
int i=1,j=0;//j指向前缀末尾位置,i指向后缀末尾位置
//为比较前缀与后缀是否相等,所以i!=j
next[0]=0;
int n=0;//n表示a的长度
for(int i=0;;i++)
{
if(a[i]=='\0')
break;
else
n++;
}
for(int i=1;i<n;i++)
{
while(a[i]!=a[j]&&j>0)//前后缀不相同
{
j=next[j-1];
}
if(a[i]==a[j])//前后缀相同
{
j++;//前缀继续向后走
next[i]=j;//填充数组
}
}
}
int KMP(char *a,char *b,int x)
{
int i=0;//i指向a,j指向b,俩个指针
int j=0;
int next[100];
get_next(a,next);
int a.length=0,b.length=0;
for(int i=0;;i++)//得到a的长度
{
if(a[i]=='\0')
break;
else
a.length++;
}
for(int i=0;;i++)//得到b的长度
{
if(a[i]=='\0')
break;
else
b.length++;
}
while(i<a.length&&j<b.length)
{
if(i=0||a[i]==b[j])
{
i++;
j++;
}
else
{
j=next[j-1];
}
}
if(j>b.length)
return i-a.length;
else
return 0;
}