题意
给定n个字符串 s[i],再给出m个查询的字符串 t[i],问 t[i] 是否为某个 s[i] 循环无限次的子串。
题解
分成两种情况
① t[i] 比 s[j] 短, 这个时候可以用后缀自动机,把每个 s[j] 重复一次,然后放到SAM中,这样直接每次直接查询就好了。当然,因为是有t(t<=1e5)组数据,全部初始化肯定会超时的,所以下一个要用到哪部分哪部分就初始化,这样最多初始化1e6次,因为所有的长度不会超过1e6。
② t[i] 比 s[j] 长,这样的话就不能用后缀自动机了,因为并不知道 t[i] 到底是 s[j] 重复了几次的子串。考虑到 t[i] 如果是某个 s[j] 循环无限次的子串,那么他一定是有循环节的,因为不知道有没有循环节,所以找到第一个最长的且每个字母只出现了一次的子串,并用最小表示法表示,然后在一颗插入了每个 s[j] 的最小表示法的字典树中查找是否有这个子串,如果有的话,构造长度为 | t[i] |,以该子串为循环节的串,如果这个串就是 t[i] 那么说明有该串,否则没有。当然,字典树的初始化也会卡,所以同样的边用边初始化下一个即将要用的。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
struct SAM
{
int mxlen[maxn<<1],link[maxn<<1],nt[maxn<<1][26];
int sz,last,len;
void init()
{
memset(nt[0],0,sizeof(nt[0]));
mxlen[0]=0;
link[0]=-1;
sz=1;
last=0;
}
void extend(int c)
{
c-='a';
int cur=sz++;
mxlen[cur]=mxlen[last]+1;
int p=last;
memset(nt[cur],0,sizeof(nt[cur]));
while(p!=-1&&!nt[p][c]){
nt[p][c]=cur;
p=link[p];
}
if(p==-1) link[cur]=0;
else{
int q=nt[p][c];
if(mxlen[p]+1==mxlen[q]) link[cur]=q;
else{
int clone=sz++;
mxlen[clone]=mxlen[p]+1;
memcpy(nt[clone],nt[q],sizeof(nt[q]));
link[clone]=link[q];
while(p!=-1&&nt[p][c]==q){
nt[p][c]=clone;
p=link[p];
}
link[q]=link[cur]=clone;
}
}
last=cur;
}
bool query(string s)
{
int p=0;
for(int i=0;i<s.size();i++){
int c=s[i]-'a';
if(!nt[p][c]) return 0;
p=nt[p][c];
}
return 1;
}
}sam;
struct Trie
{
int tree[maxn][30];
int color[maxn];
int k=1;
int newnode()
{
memset(tree[k],0,sizeof(tree[k]));
color[k]=0;
return k++;
}
void init()
{
color[0]=0;
memset(tree[0],0,sizeof(tree[0]));
k=1;
}
void insert(string a)
{
int p=0;
int len=a.size();
for(int i=0;i<len;i++){
int c=a[i]-'a';
if(!tree[p][c]) {
tree[p][c]=newnode();
}
p=tree[p][c];
}
color[p]++;
}
int query(string a)
{
int p=0;
int len=a.size();
for(int i=0;i<len;i++){
int c=a[i]-'a';
if(!tree[p][c]) return 0;
p=tree[p][c];
}
return color[p];
}
}trie;
const int maxm=1e5+10;
string s[maxm];
string get_min(string s)
{
string t;
int p=26,pos=0;
for(int i=0;i<s.size();i++){
if(s[i]-'a'<=p) p=s[i]-'a',pos=i;
}
t=s.substr(pos);
t+=s.substr(0,pos);
return t;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
int k=1;
while(T--){
sam.init();
trie.init();
cout<<"Case #"<<k++<<":"<<endl;
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>s[i];
string t=get_min(s[i]);
trie.insert(t);
}
for(int i=0;i<n;i++){
sam.last=0;
for(int j=0;j<s[i].size();j++) sam.extend(s[i][j]);
for(int j=0;j<s[i].size();j++) sam.extend(s[i][j]);
}
int m;
cin>>m;
while(m--){
string a;
cin>>a;
if(sam.query(a)){
cout<<"YES"<<endl;
continue;
}
int pos=a.size();
for(int i=1;i<pos;i++){
if(a[i]==a[0]){
pos=i;
break;
}
}
string t=a.substr(0,pos);
string tt=get_min(t);
if(trie.query(tt)){
string p;
for(int i=0;i<a.size();i++){
p+=t[i%t.size()];
}
if(p==a){
cout<<"YES"<<endl;
continue;
}
}
cout<<"NO"<<endl;
}
}
return 0;
} 
京公网安备 11010502036488号