Kmp--P3375 【模板】KMP字符串匹配
把已经给出的串称为文本串,要在文本串中找的串称为模式串。特别地,前两位$(kmp[0],kmp[1])$为零。比如串$ABABAC$,它的$fail$应该为001230。
已知第$i$位的自动机$kmp[i]$,要求第$i+1$位的。设$j=kmp[i]$。$kmp[i]$一定在i前面,$kmp[i]$已经求好了。如果字符串的$i$和$j+1$位不同$(s2[i]≠s2[j+1])$,那么j不断地跳到自己的自动机$kmp[j]$,直到匹配上或$j=0$为止。如果匹配上,$f[i] = j+1$;否则如果还是$s2[i]≠s2[j+1]$,说明是$j=0$而不是匹配上了,那么$kmp[j] = 0$。
构建自动机:
1 j=0; 2 for (int i=2;i<=lb;i++){ 3 while(j&&b[i]!=b[j+1]) 4 //此处判断j是否为0的原因在于,如果回跳到第一个字符就不 用再回跳了 5 j=kmp[j]; 6 //通过自己匹配自己来得出每一个点的kmp值 7 if(b[j+1]==b[i])j++; 8 kmp[i]=j; 9 //i+1失配后应该如何跳 10 }
我们已经对该子串构建了一个自动机,当两个字符串相比较,如果失配,即$s1[i]!=s2[j+1]$就跳回,此时一定两位匹配$(j!=0)$或从头开始$(j=0)$,之后再匹配下一位即可。
运行自动机:
1 int j; 2 j=0;//j可以看做表示当前已经匹配完的模式串的最后一位的位置 3 //也可以理解为j表示模式串匹配到第几位了 4 for(int i=1;i<=la;i++){ 5 while(j&&b[j+1]!=a[i])j=kmp[j]; 6 //如果失配 ,那么就不断向回跳,直到可以继续匹配 7 if (b[j+1]==a[i]) j++; 8 //如果匹配成功,那么对应的模式串位置++ 9 if (j==lb) { 10 cout<<i-lb+1<<endl; 11 j=kmp[j]; 12 //继续匹配 13 } 14 }
完整代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 using namespace std; 6 #include<string> 7 const int maxn = 1e6+10; 8 int len1,len2,f[maxn]; 9 string s1,s2; 10 void fail() { 11 f[0] = f[1] = 0; 12 for(int i = 1; i < len2; i++) { 13 int j = f[i]; 14 while(j && s2[i]!=s2[j]) j = f[j]; 15 if(s2[i] == s2[j]) f[i+1] = j+1; 16 else f[i+1] = 0; 17 } 18 } 19 void kmp() { 20 int j = 0; 21 for(int i = 0; i < len1; i++) { 22 while(j && s1[i]!=s2[j])j = f[j]; 23 if(s1[i] == s2[j])j++; 24 if(j == len2) { 25 printf("%d\n",1+i-len2+1); 26 j = f[j]; 27 } 28 } 29 } 30 31 int main() { 32 cin>>s1>>s2; 33 len1 = s1.length(); 34 len2 = s2.length(); 35 fail(); 36 kmp(); 37 for(int i = 1; i <= len2; i++) 38 printf("%d ",f[i]); 39 return 0; 40 }