[模板]KMP算法

来源:互联网 发布:电脑上的健身软件 编辑:程序博客网 时间:2024/06/05 08:45

    • 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串已经整完了),并且可以根据这时的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个元素下标是0for(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;//边界-1for(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数组}
原创粉丝点击