写在最前面:
此系列中的所有模板全为大佬所写,我只是一个搬运工(?)。
记录
kmp一般用来比较两组字符串(比如S、P)在哪一段相同。如果比较的时候不相同,那么指向P的指针会指向和暂停点前面和最开始相同的一段然后再次和S比较,直到找出来相等为止。一般统计在该点的重复数组有几个的时候用数组ne[i]表示。
下面举个例子:
例如:
S为abcababa;P为ababa。这里选取的均为从1开始读入。
1.计算ne[]的时候,利用双指针。i从2开始(因为第一个和自身相等没意义),j从0开始。
首先比较p[j+1]和p[i]的关系,这里即为a(1)和b(2),不相等,所以ne[i]=j=0,然后使得i++指向下一个字母。(注:这里的括号内的数字表示在字符串P内的序号,下同)
第二轮比较为a(1)和a(3),相等,j++,然后ne[2]就等于1。
第三轮先比较b(2)和b(4),相等,j++,ne[3]就等于2。
……
以此类推。
2.进行s和p的比较。
和上方类似的比较过程,这里不在阐述。值得一提的是,如果没有匹配成功,那么j指针将自动返回到与上述区间相同的点。
比如说当s中的i指向c(3)的时候,和p数列的a(3)不相等,这时候j就会更新到ne[2]=1(j+1=3),也就是把p重新从a(1)开始进行下一轮比较。直到得出答案。
下面是模板:
#include <iostream>
#include <cstdio>
using namespace std;
const int N=1e6;
int n,m;
char p[N],s[N];
int ne[N];
int main()
{
scanf("%d",&n);
getchar();
for(int i=1; i<=n; i++)
scanf("%c",&p[i]);
cin>>m>>(s+1);//和上面读入方法相同,只不过换了一种写法
for(int i=2,j=0; i<=n; i++)
{
while (j&&p[i]!=p[j+1]) j=ne[j];//不相等则退一步,直到退到无路可退(j=0)
if(p[i]==p[j+1]) j++;
ne[i]=j;
}
for(int i=1,j=0; i<=m; i++)
{
while (j&&s[i]!=p[j+1]) j=ne[j];
if(s[i]==p[j+1]) j++;
if(j==n)//如果相同的数量和p字符串数量相同,则输出满足条件的第一个字符的序号,且同时更新
{
printf("%d ",i-n);
j=ne[j];//为下一次的移动做准备
}
}
cout << endl;
return 0;
}