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]); }