题目链接:https://vjudge.net/contest/361017#problem/E
题意:在一个字符母串中,找到两个不重叠的子串可以拼成要求得到的美好的 字符串。
第一种做法:双向KMP
参考:https://blog.csdn.net/acm_cxlove/article/details/8440574
str为母串,_str为母串的倒序
p为所要找的子串,pp为所要找的子串的倒序
L[i]表示p串中前i个字符在str串中最早出现的位置
R[i]表示pp串中前i个字符在_str串中最早出现的位置
也就是从左边和右边分别找到p串两部分最早出现的位置,然后比较这两部分是否重叠,如果没有重叠的话就满足一种情况
#include<iostream>
#include<cstdio>
using namespace std;
#define maxn 100100
#define inf 1e9
int m;
char str[maxn];
char _str[maxn];
char p[maxn];
char pp[maxn];
int Next[maxn];
int L[maxn];
int R[maxn];
int cnt;
void get_next(char *p,int len)
{
Next[0]=-1;
int i=0,j=-1;
while(i<len)
{
if(j==-1||p[i]==p[j])
{
i++;j++;
if(p[i]==p[j])
Next[i]=Next[j];
else
Next[i]=j;
}
else
j=Next[j];
}
}
void match(char *p,int plen,char *str,int slen,int *dp)
{
//dp[i]表示p串中前i个字符一同最早出现在str串的第dp[i]个字符串
//利用了KMP找字符串匹配问题
int i=0,j=0;
dp[0]=1;
while(i<plen&&j<slen)
{
if(i==-1||p[i]==str[j])
{
i++;j++;
dp[i]=min(dp[i],j);
}
else
i=Next[i];
if(i==plen)
i=Next[i];
}
}
int main()
{
scanf("%s",str);
cin>>m;
int slen=strlen(str);
//存储倒序的母串
for(int i=0;i<slen;i++)
_str[i]=str[slen-1-i];
for(int i=0;i<m;i++)
{
scanf("%s",p);
int plen=strlen(p);
if(plen==1)
continue;
//初始化
memset(Next,0,sizeof(Next));
for(int j=0;j<=plen;j++)
{
L[j]=inf;
R[j]=inf;
}
//处理正常顺序子串与母串
get_next(p,plen);
match(p,plen,str,slen,L);
//pp为p翻转之后的字符串
for(int j=0;j<plen;j++)
pp[j]=p[plen-1-j];
//处理倒序子串与母串
memset(Next,0,sizeof(Next));
get_next(pp,plen);
match(pp,plen,_str,slen,R);
//遍历每一个点进行匹配
//L[i]表示p[0]~p[i-1]在母串的最早出现位置
//R[i]表示p[plen-1]~p[plen-i-2]在母串中从右向左最早出现的位置
//如果i点处分割开的左串和右串在母串中出现的位置没有交集,则可以
for(int j=1;j<=plen;j++)
{
if(L[j]+R[plen-j]<=slen)
{
cnt++;
break;
}
}
}
cout<<cnt<<endl;
return 0;
}
第二种:后缀自动机
后缀自动机我还正在学,后期就补上