KMP算法理解

KMP算法的关键之处在于next数组的理解!

假设t为目标,p为模式。j为t中下标,i为p中下标。

先从朴素模式匹配算法理解下或许会好些:

当模式串中的p[i]与目标串中的t[j]不等时,需要把目标中的指针回朔到j-i+1处重新用t[j-i+1]与p[0]匹配。在目标指针回朔的过程中,就可能导致许多不必要的重复匹配。

 

KMP算法的思想是:当p[i]!=t[j]时,要能找到模式串一个大于等于1的右移位数(在朴素模式下总是右移1位),并且确定p和t继续比较的字符。最为理想的情况就是,希望匹配过程对于t是无回朔的。也就是说在,在(模式串)右移若干位后,应该立即用p中一个新的字符p[k](显然k<i)和t[j](甚至t[j+1])继续比较。

经过前辈们的研究发现这个k值不仅存在,而且仅依赖于模式串p本身(也就是说一旦模式串p一确定,对于每个字符就有不同的固定的k值),与目标串t无关。因此可以在与任何目标串匹配前计算出来。

 

先来看看这个k值的存在性:当p[i]!=t[j]时,假设在p[i]前有匹配的字符,即i>0(如果没有匹配的字符,即i=0,此时目标串指针肯定不用回朔,直接用t[j+1]和p[0]比较),此时,p[0]~p[i-1]和t[j-i]~t[j-1]匹配。这就给我们提供了一种思路,可以通过分析p[0]~p[i-1]来分析t[j-i]~t[j-1]。

 

如果模式串中存在这样一个k值,使得p[0]~p[k-1]=p[i-k]~p[i-1](共k个匹配字符,且k是满足这个关系的最大值),则可以知道p[0]~p[k-1]和t[j-k]~t[j-1]匹配。因此可以直接把模式串右移i-k位,使得p[0]~p[k-1]和t[j-k]~t[j-1]匹配,直接比较p[k]和t[j](当右移量小于i-k时,也用不着比较,因为根据k的意义,此时用长度大于k的前后缀对齐,比较结果必定不等;当右移量大于i-k时,又可能错过目前匹配成功的机会)。此时next[i]=k;

 

如果不存在这样的k值,也就是说p[0]开始的任何字串不等于p[i-1]结尾的任何字串。基于p[0]~p[i-1]和t[j-i]~t[j-1]匹配的事实,可以说p[0]开始的任何字串不等于t[j-1]结尾的任何字串,这说明主串没有必要回朔到t[j-i]~t[j-1],既然没有必要回朔并且p[i]!=t[j],则t[j]直接与p[0]比较。此时next[i]=0。

 

特殊情况,i=0时,p[i]=p[0]!=t[j],则变成p[0]和t[j+1]比较。为了统一令next[0]=-1,所以在kmp的算法设计中,判断到i=-1时,可以用i++,j++使得i=0,j=j+1形成p[0]与t[j+1]比较的形式。

 

理解了next数组,理解kmp算法就轻而易举了。

kmp算法:

{

int  i,j;

i=0;j=0;

while(i<p->n&&j<t->n)

if(i==-1||p->c[i]==t->c[j])

{i++;j++;}

else i=next[i];

if(i>=p->n)

return(j-p->n+1);

else return(0);

}

 

再来看看计算next数组的算法:

从上面的推导,next[0]=-1;

当next[i]=k(说明p[0]~p[k-1]=p[i-k]~p[i-1])时,求next[i+1]=?(k<i)

当p[i]=p[k]时,说明p[0]~p[k-1]p[k]=p[i-k]~p[i-1]p[i],则next[i+1]=k+1;

当p[i]!=p[k]时,则说明p[0]~p[k-1]p[k]!=p[i-k]~p[i-1]p[i],显然这又是一个模式匹配问题(p同时为模式串和目标串),从kmp算法看此时应该用p[next[k]](注意理解下这儿的next[k])与p[i]比较,使k=next[k](next[k]<k,k在逐渐递减),再用p[i]与p[k]比较直到相等或者k=-1为止。如果k=-1,表示没有k使p[0]开始的任何字串等于p[i]结尾的任何字串,所以直接用p[i]与p[0]比较,则next[i+1]=0=-1+1。如果某个k使p[i]=p[k],则next[i+1]=k+1(注意此时的k不等于一开始的k,因为k在逐渐递减)。所以next[i+1]=k+1。再次注意,k已经变化。

next的算法:

{

int i=0,k=-1;

next[0]=-1;

while(i<p->n-1)

{

while(k>=0&&p->c[i]!=p->c[k])  k=next[k];

i++;j++;

next[i]=k;

}

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值