KMP

KMP学了好几遍了,今天才明白的差不多...

留篇博客,免得自己以后忘了qaq

我们先讨论next的含义:

next[i] : 在0<=j<i中的最大的j,满足0-i中,使长度为j的后缀和长度为j的前缀相同

接下来我们讨论两个问题:1.怎么求next; 2.有了next怎么求匹配

定义:next[1] = 0(由定义),next[0]不定义,默认为0

问题1:怎么求next

考虑目前到i + 1,令j=next[i],那么如果a[j + 1] != a[i + 1],说明此时的next[i]的下一位与a[i + 1]不等,即next[i + 1] != next[i] + 1了

那么和0-i的这一段后缀相等的最大前缀只能是next[j]即next[next[i]],那么令j=next[j],继续进行上述判断,直到j=0无法判断为止

问题2:有了next怎么求匹配

考虑目前匹配到模版串的j,原串的i,如果a[j] == a[i],自然这一位相等,令j++

否则,已经匹配的一段j中,有一部分和原串之前匹配的部分是相等的,这个位置就是next[j](显然的由next的定义,这个长度是合法且极大的)

这样问题就可以解决了

考虑kmp的复杂度,在问题2中,我们至多只令j增加n次,而虽然在令j=next[j]的过程中可能有多次运算,但可以通过势能分析证明

我们将j=next[j]的复杂度摊到每次j++,那么每次操作平均复杂度至多是2,总复杂度O(n+m)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=1e7+1;
int next[maxn],j=0;
char s1[maxn],s2[maxn];
int main()
{
    scanf("%s",s1+1);
    scanf("%s",s2+1);
    int len1=strlen(s1+1),len2=strlen(s2+1);
    //cout<<len1<<" "<<len2;
    for(int i=2;i<=len2;i++)
    {
        while(j&&s2[i]!=s2[j+1]) j=next[j];
        if(s2[i]==s2[j+1]) j++;
        next[i]=j;
    }
    j=0;
    for(int i=1;i<=len1;i++)
    {
        while(j&&s1[i]!=s2[j+1]) j=next[j];
        if(s1[i]==s2[j+1]) j++;
        if(j==len2) 
        {
            printf("%d
",i-len2+1);
            j=next[j];
        }
        //cout<<"hahha"<<endl;
    }
    for(int i=1;i<=len2;i++) printf("%d ",next[i]);
}
View Code
原文地址:https://www.cnblogs.com/LM-LBG/p/10801925.html