CF149E题解—KMP算法
题意
给出一个长字符串S和一组询问字符串P,对于每个询问需要知道在S中是否存在两个位置不同的子串可以组成该询问字符串。
分析:
运用KMP算法对正串S和P进行匹配,用数组lo[x]记录第一次(第一次很关键)匹配的长度为x时其匹配在S中的末位置,再将S和P逆置,运用KMP算法再进行匹配,对于每一次匹配长度为x,查找对应剩下的长度m-x(m为P串长)是否已经存在,再加判断位置是否越界即可。
我的代码
#include<iostream>
#include<string>
#include<algorithm>
#include<string.h>
#define N 1005
using namespace std;
int dp[N],lo[N];
void getdp(string s){ //求next数组
memset(dp,0,sizeof(dp));
dp[0]=-1;
int len=s.length(),k=-1,j=0;
while(j<len){
if(k==-1||s[k]==s[j]){
k++;j++;
dp[j]=k;
}
else{
k=dp[k];
}
}
}
int kmp(string t,string p,int judge){
int i=0,j=0,n=t.length(),m=p.length();
int maxj=-1;
getdp(p);
while(i<n&&j<m){
if(j==-1||t[i]==p[j]){
i++;j++;
}
else{
j=dp[j];
}
if(judge==1&&j!=-1&&j!=0&&!lo[j]) //匹配正串记录第一次匹配长度为j时在样本串中的位置
lo[j]=i;
if(judge==2&&j!=-1&&j!=0&&lo[m-j]&&n-i+1>lo[m-j]) //匹配逆置的模式串是否成功
return 1;
}
return 0;
}
int main(){
string t,p,temp;
int nu,ans=0;
cin>>t>>nu;
if(t.length()==1){ //样本串长度为1是直接输出答案
cout<<"0"<<endl;
return 0;
}
temp.assign(t.rbegin(),t.rend()); //逆置样本串
while(nu--){
memset(lo,0,sizeof(lo));
cin>>p;
kmp(t,p,1); //匹配正串
reverse(p.begin(),p.end());
if(kmp(temp,p,2)) //匹配逆置的模式串
ans++;
}
cout<<ans<<endl;
}