您的位置:首页 > 其它

[模板]KMP算法

2017-10-13 13:17 197 查看
KMP算法简介

KMP算法代码

预处理next数组

KMP算法简介

什么是KMP算法???

KMP算法是由分别姓K/M/P的三个人提出来的。这个算法主要应用于字符串之间的匹配用的。有人会说:字符串匹配??string类型不是有个成员函数find呢?咳咳,我先要说明的是,c++std里面自带的库函数肯定都是时间复杂度超级高的,如果真的有逆天的函数,时间复杂度很优秀的库函数,CCF是不让用的,比如CCF已经禁用了所有以下划线开头的库函数,例如__gcd()函数。因为pascal真的这种东西什么都没有,为了保持相对平衡,不让用这种东西。

好吧言归正传:

KMP跟传统的暴力枚举匹配的一个最大的空间上的区别就是多了一个next数组,这个next数组起到的作用是:比较A中有没有子串B的过程中,在发现两个字符不相匹配的时候,指向字符串A的指针不变,指向字符串B的指针就向前跳几个单位,保证这个指针以及之前的B串都是跟A的子串匹配的。而next数组就是用来存跳的单位数。

引用一个大佬的例子

假如,A=”abababaababacb”,B=”ababacb”,我们来看看KMP是怎么工作的。我们用两个指针i和j分别表示,A[i-j+ 1..i]与B[1..j]完全相等。也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就说B是A的子串(B串已经整完了),并且可以根据这时的
4000
i值算出匹配的位置。当A[i+1]<>B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j’]保持匹配且新的B[j’+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。我们看一看当 i=j=5时的情况。

i = 1 2 3 4 5 6 7 8 9 ……

A = a b a b a b a a b a b …

B = a b a b a c b

j = 1 2 3 4 5 6 7

此时,A[6]<>B[6]。这表明,此时j不能等于5了,我们要把j改成比它小的值j’。j’可能是多少呢?仔细想一下,我们发现,j’必须要使得B[1..j]中的头j’个字母和末j’个字母完全相等(这样j变成了j’后才能继续保持i和j的性质)。这个j’当然要越大越好。在这里,B [1..5]=”ababa”,头3个字母和末3个字母都是”aba”。而当新的j为3时,A[6]恰好和B[4]相等。于是,i变成了6,而j则变成了 4:

i = 1 2 3 4 5 6 7 8 9 ……

A = a b a b a b a a b a b …

B = a b a b a c b

j = 1 2 3 4 5 6 7

从上面的这个例子,我们可以看到,新的j可以取多少与i无关,只与B串有关。我们完全可以预处理出这样一个数组P[j],表示当匹配到B数组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。

再后来,A[7]=B[5],i和j又各增加1。这时,又出现了A[i+1]<>B[j+1]的情况:

i = 1 2 3 4 5 6 7 8 9 ……

A = a b a b a b a a b a b …

B = a b a b a c b

j = 1 2 3 4 5 6 7

由于P[5]=3,因此新的j=3:

i = 1 2 3 4 5 6 7 8 9 ……

A = a b a b a b a a b a b …

B = a b a b a c b

j = 1 2 3 4 5 6 7

这时,新的j=3仍然不能满足A[i+1]=B[j+1],此时我们再次减小j值,将j再次更新为P[3]:

i = 1 2 3 4 5 6 7 8 9 ……

A = a b a b a b a a b a b …

B = a b a b a c b

j = 1 2 3 4 5 6 7

现在,i还是7,j已经变成1了。而此时A[8]居然仍然不等于B[j+1]。这样,j必须减小到P[1],即0:

i = 1 2 3 4 5 6 7 8 9 ……

A = a b a b a b a a b a b …

B = a b a b a c b

j = 0 1 2 3 4 5 6 7

终于,A[8]=B[1],i变为8,j为1。事实上,有可能j到了0仍然不能满足A[i+1]=B[j+1](比如A[8]="d"时)。因此,准确的说法是,当j=0了时,我们增加i值但忽略j直到出现A[i]=B[1]为止。


KMP算法代码

int j=-1;//边界因为string的第1个元素下标是0
for(int i=0;i<a.size();i++)//枚举字符串A
{
while ((j>-1)&&(b[j+1]!=a[i]))   j=next[j];//如果j>-1就代表已经不是第一次的查找了,如果b[j+1]!=a[i]就代表这个字符串的某个元素不匹配,j需要通过next数组进行向前跳
if (b[j+1]==a[i])  j++;
if (j==b.size()-1) //找到匹配,当然可以继续找下一个匹配
{
cout<<i-m<<endl;
j=next[j];
//最后的j=next[j]是为了让程序继续做下去,因为我们有可能找到多处匹配
}
}


预处理next数组

对于next数组的预处理也是b串的自我匹配的过程:

next[0]=-1;  //string 的第一个元素下标是0,所以边界设为-1;
int j=-1;//边界-1
for(int i=1;i<b.size();i++)
{
while ((j>=0)&&(b[j+1]!=b[i]))   j=next[j];//如果不匹配就向前跳
if (b[j+1]==b[i])  j++;//如果匹配就累加
next[i]=j;//更新next数组
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: