kmp 基础详解

来源:互联网 发布:2020软件 编辑:程序博客网 时间:2024/06/05 11:47

断更许久。。。开学了对不对要拼命
刷(wan)题(you)了(xi)
不能浪费大好青春啊的!

我旁边这位小同学讲了一个星期的kmp终于才讲清楚了QAQ
(好像不能叫小同学他好像还比我大QAQ)
作为巩固决定写篇文章来温习一下
我尽我所能写好吧

之所以叫做KMP,是因为这个算法是由Knuth、Morris、Pratt三个提出来的,取了这三个人的名字的头一个字母。
讲那么多不就是处理字符串的一个东东嘛
比如说判断B串是否为A串的字串
为什么说他好呢
“因为快啊”
确实,KMP的最坏情况也只需要O(nm)

所以….废话讲完了

先贴代码
…..还有题目 poj3461 Oulipo

#include <cstdio>#include <cstring>using namespace std;int i,j,lena,lenb,n,ans,p[10010];char a[1000010],b[10010];int main(){    scanf("%d",&n);    while (n--)    {        scanf("%s",b+1);lenb=strlen(b+1);        scanf("%s",a+1);lena=strlen(a+1);        p[1]=0;j=0;        for (i=2;i<=lenb;i++)         {            while (j>0&&b[i]!=b[j+1]) j=p[j];            if (b[i]==b[j+1]) j++;            p[i]=j;        }        j=0;ans=0;        for (i=1;i<=lena;i++)        {            while (j>0&&a[i]!=b[j+1]) j=p[j];            if (a[i]==b[j+1]) j++;            if (j==lenb)            {                ans++;            }        }        printf("%d\n",ans);    }    return 0;}

我们用两个指针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能继续增加)。

举个栗子

A=”abababaababacb”,B=”ababacb”,

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 bj = 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 bj =     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 bj =     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 bj =         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 bj =             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 bj =             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]为止。

这就是以下这一段

    for (i=1;i<=lena;i++)    {        while (j>0&&a[i]!=b[j+1]) j=p[j];        if (a[i]==b[j+1]) j++;        if (j==lenb)        {            ans++;        }    }

那么我们怎么快速知道要跳到哪一格呢?
很简单吗
预处理一遍B
即复制一个B,存为B’
然后让B和B’来比较
不懂得可以再看一遍上面的解释。

    p[1]=0;j=0;    for (i=2;i<=lenb;i++)     {        while (j>0&&b[i]!=b[j+1]) j=p[j];        if (b[i]==b[j+1]) j++;        p[i]=j;    }

这里的p[i]=j是为了能让程序继续做下去,因为我们有可能找到多处匹配。所以为了避免卡掉… 0.0
所以….完了

本文章大部分来自Matrix67
所谓我的巩固就是在写的同时顺便看多一次

0 0
原创粉丝点击