写了几种版本,首先是string版的。string大法吼哇!!!
这里的next1[i]指的是在0~i-1构成的字符串中有多大长度的相同前缀后缀,同时也是模式串str2[i]匹配失败后跳转到的位置。同时值得注意的是这里的字符串是从0开始的
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
int next1[1000005];
void getnext(string str)
{
next1[0]=-1;
int k=-1,j=0;
while(j<str.size()-1){
if(k==-1||str[k]==str[j]){
k++;j++;
next1[j]=k;
}
else{
k=next1[k];
}
}
}
int main()
{
string str1,str2;
cin>>str1>>str2;
getnext(str2);
int i=0,j=0;
while(i<str1.size()){//因为是输出s1中所有s2的位置所以while里只对s1的长度做出限制
if(j==-1||str1[i]==str2[j]){
if(j==str2.size()-1){//判断一定要放在这里,否则i++,j++会导致重叠部分无法利用,比如ABABABAB和ABAB的匹配
cout<<i-str2.size()+1<<endl;
j=next1[j];continue;
}
i++;j++;
}
else{
while(j>=0&&str2[j]!=str1[i])//这里注意要加等于号
j=next1[j];
}
}
// if(j==str2.size()){
//
// }
for(i=0;i<str2.size();i++)
cout<<next1[i]<<' ';
return 0;
}
字符串从1开始的版本,By Poeroz
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=1000005;
int n,m,j,cnt;
char A[N],B[N];
int next1[N];
int main()
{
scanf("%s%s",A+1,B+1);
n=strlen(A+1);m=strlen(B+1);
next1[1]=0;j=0;
for(int i=2;i<=m;i++){
while(j&&B[j+1]!=B[i])j=next1[j];
if(B[j+1]==B[i])j++;//避免j=0的情况
next1[i]=j;
}
j=0;
for(int i=1;i<=n;i++){
while(j&&B[j+1]!=A[i])j=next1[j];
if(B[j+1]==A[i])j++;
if(j==m){
cout<<i-m+1<<endl;
cnt++;j=next1[j];
}
}
//cout<<cnt<<endl;
for(int i=1;i<=m;i++)
cout<<next1[i]<<' ';
return 0;
}