如果要匹配一个字符串,例如我们要匹配s[m]中哪些位置可以匹配p[n],暴力的做法应该是对s[m]中的所有位置遍历,每个位置都匹配一遍,这样做是可以,但是效率很低。如果我们在每个位置匹配的时候能够利用好已经部分匹配好的信息,例如s[i-1]和p[j]和前面的都匹配好了,但是是s[i]和p[j+1]不匹配,可以利用前面匹配好的部分对j进行跳转,这就是KMP算法的核心所在。
ne数组:首先我们都规定,数组下标都从1开始,next数组的含义即——
对next[ j ] ,是p[ 1, j ]串中前缀和后缀相同的最大长度(部分匹配值), 即 p[ 1, next[ j ] ] = p[ j - next[ j ] + 1, j ]。这样在出现上面的情况时(s[i-1]和p[j]和前面的都匹配好了,但是是s[i]和p[j+1]不匹配)就可以对j进行跳转:j=ne[j]。
怎么求ne数组:这就需要让p数组自己匹配自己,从最开始匹配肯定时没问题的所以应该是从2开始,即i从2开始,j从0开始,让j指向的取匹配i指向的。
下面是详细的代码:
#include<iostream>
using namespace std;
const int N=1e5+10;
const int M=1e6+10;
char p[N],s[M];
int ne[N];
int main()
{
int n,m;
cin>>n>>p+1>>m>>s+1;
//ne[n]=0因为整个p数组的最大前缀和后缀就是p数组本身
//i所指的是后缀,j所指的是前缀
//j所指的匹配取匹配i所指的
for(int i=2,j=0;i<=n;i++)
{
//不匹配,则对j进行跳转,如果j为0,则不存在前缀和后缀相等的情况
//也就是说需要重新匹配,需要跳转到最开始.
while(j&&p[i]!=p[j+1])j=ne[j];
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)cout<<i-j<<' ';
}
return 0;
}