KMP算法:
KMP也是字符串匹配算法,效率比BF算法高,因为主串不回退,KMP算法的核心是找到模式串回退的位置
KMP算法的核心思想是主串不回退,模式串回退
回退点的处理方式:如果已匹配部分主串的后缀和模式串的前缀刚好相同,回退的位置在失配前已匹配部分的最大前 缀和最大后缀相同的个数,如下图:
KMP算法图解:
KMP算法的关键在于求解适配点之前的以匹配部分是否存在最大前缀和最大后缀相等,如果相等则模式串的j回退到最大前缀和最大后缀相等的个数的位置,比如有三个相等,则j回退到三号下标
next数组的求解:next数组保存模式串中每一个失配点模式串应该回退的位置
- 手动计算next数组的值
如果第一个位置失配,前面没有已匹配部分,因此为-1;如果第二个位置失配,前面只有一个已匹配,针对一个字符不存在最大前缀和最大后缀,因此为0;模式串前面两个要回退的位置是固定的,第一个为-1,第二个为0;如果第三个失配,前面有两个已匹配的字符,但没有最大前缀和最大后缀相同的,因此为0;依次推算。
2、next数组的求解
- 当串中元素相等时
next[0]=-1; next[1]=0; 求next[j+1]的话,肯定会已知next[j]=k, k为最大前缀和最大后缀相等的个数
if ( t[k] == t[j] ) next[j+1]=k+1,如果前缀的下一个(t[k])和后缀(t[j])的下一个相等,则next[j+1]的最大前缀加1。
- 当串中元素不相等时 t[k] != t[j]
KMP算法在失配后,会让模式串回退到最大前缀和最大后缀相等的个数的位置,上述拿最大前缀当模式串去匹配最大后缀,2号下表前面有两个元素,匹配失败,没有最大前缀和最大后缀,所以k要回退的位置在0号下标,就在next[2]中保存,所以k=next[2]
KMP算法的代码:
void getNext (char* t,int next[])
{
int len = strlen(t) ;
int j=1;
int k=0;
if (len == 0)
{
return;
}
next[0] = -l;
if (len > 1)
{
next[1] = 0;
}
while(j〈len-1)
{
if (k == -1 || t[k] == t[j])//所有的匹配都已完成,没有任何最大的前缀和最大后缀相等
{
next[j+1]=k+1;
j++;
k++ ;
}
else
{
k = next[k];
}
}
}
void showNext(int next[],int len)
{
for(int i = 0;i < len; i++)
{
printf("%d",next[i]);
}
printf("\n");
}
int KMP (char* s, char* t)
{
int slen=strlen(s);//主串长度
int tlen=strlen(s);//模式串长度
int i=0;//主串下标
int j=0;//模式串下标
int next=(int*)malloc(sizeof(int*tlen));
getNext(t,next);
showNext(next,tlen);
while(i<slen&&j<tlen)
{
if(j==-1||s[i]==t[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
if(j==tlen)
{
return i-tlen;
}
}
int main()
{
char* s="ABCABCABCCABCABCD";
char* t="ABCABCD";
int index=KMP(s,t);
printf("%d\n",index);
return 0;
}