题意

给定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;
}