这个题,感觉特别神奇,感觉复杂度是正确的,但是给不出特别正确的证明。
题意:给你n个匹配串,给你m次询问,然后问询问串里有没有子串是某个匹配串的映射?
最开始就感觉多模式匹配肯定是ac自动机嘛,然后发现映射情况数太多不好处理,就想有没有什么方法能消除字母与字母之间的影响,想了想口胡了一种做法,就是把考虑每个字母上次出现的位置,然后表示成位置差,这样一来每个串就可以o(1)的映射成一个唯一的数字串。
但是这样转化的话,首先是动态开店,不过感觉每个节点的指针不会太多,这个log还是很小的。再之后, 就感觉ac自动机的fail树不是特别好建,因为动态开点的,很可能我有的指针我的fail没有。然后我脑子一热就让每个点继承上一个点的状态了,不是很会证这个复杂度,但是明显能感觉出很作死的样子,然后果然tle了。
去网上找了找题解,发现有一个大佬是差不多的思路,然后他是每次向上暴力跳fail的嘛,他也说给不出复杂度的证明。这个写法是能ac的。
考虑了一些情况,感觉暴力跳的话应该是达不到(n^2)的,这里给出一个不一定正确的想法。考虑只有一条链时的fail树,大概应该是o(n)的,因为在这一次我跳过的路径,其他点不会再跳一遍。然后考虑n个串的时候,感觉上最差是o(nsqrt(n))的,但是给不出证明。然后题目保证匹配串只有5000个,也不知道是不是这里的保证。
还有一个细节,因为是靠记录上一个出现的位置转化的字符串,这样在失配的时候很可能当前位置变成未出现过的了,要特判一下。
#include <bits/stdc++.h>
using namespace std;
#define N 200025
#define M 27
#define ll long long
#define mod 1000000007
#define go(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define inf 0x3f3f3f3f
#define ld long double
#define pii pair<int,int>
#define vi vector<int>
#define add(a,b) (a+=(b)%mod)%=mod
#define lowb(c,len,x) lower_bound(c+1,c+len+1,x)-c
#define uppb(c,len,x) upper_bound(c+1,c+len+1,x)-c
#define ls i*2+1
#define rs i*2+2
#define mid (l+r)/2
#define lson l,mid,ls
#define rson mid+1,r,rs
//#define root 1,n,0
#define ms(a,b) memset(a,b,sizeof a)
#define muti int T,cas=1;cin>>T;while(T--)
#define lll __int128
#define si short int
#define fi first
#define se second
#define l(x) (x&-x)
#define G(x) for( int i=h[x];i;i=eg[i].y )
map<int,int>::iterator it;
struct no{
int en[N],len[N],fail[N],l,root,now;
map<int,int>ch[N];
int newnode(int dep){
ch[l].clear();en[l]=0;len[l]=dep;
return l++;
}
void init(){
l=0;
root=newnode(0);
}
int getlast(int u,int d){
if(d>=len[u]+1)d=0;
while(u!=root&&!ch[u].count(d)){
u=fail[u];
if(d>=len[u]+1)d=0;
}
return ch[u][d];
}
void inset(int d){
//cout<<d<<endl;
if(!ch[now].count(d))ch[now][d]=newnode(len[now]+1);
//cout<<now<<' '<<l<<endl;
now=ch[now][d];
//cout<<now<<endl;
}
void build(){
queue<int>q;
fail[root]=0;
it=ch[root].begin();
while(it!=ch[root].end()){
fail[it->se]=root;
q.push(it->se);
it++;
}
while(!q.empty()){
int now=q.front();q.pop();
en[now]|=en[fail[now]];
// cout<<now<<" "<<fail[now]<<"++++++++"<<endl;
it=ch[now].begin();
while(it!=ch[now].end()){
fail[it->se]=getlast(fail[now],it->fi);
// cout<<it->fi<<' '<<it->se<<endl;
q.push(it->se);
it++;
}
}
}
int query(int d){
if(d>len[now])d=0;
now=getlast(now,d);
return en[now];
}
}ac;
int n,m,las[M];
char s[N];
int main()
{
muti{
scanf("%d",&n);
ac.init();
go(i,1,n){
go(j,0,25)las[j]=0;
scanf("%s",s+1);
int len=strlen(s+1);
ac.now=ac.root;
go(j,1,len){
if(!las[s[j]-'A'])ac.inset(0);
else ac.inset(j-las[s[j]-'A']);
las[s[j]-'A']=j;
}
ac.en[ac.now]=1;
}
// cout<<1111111111111<<endl;
ac.build();
scanf("%d",&m);
printf("Case #%d:",cas++);
go(i,1,m){
go(j,0,25)las[j]=0;
scanf("%s",s+1);
int len=strlen(s+1),flag=0;
ac.now=ac.root;
go(j,1,len){
if(!las[s[j]-'A'])flag=ac.query(0);
else flag=ac.query(j-las[s[j]-'A']);
las[s[j]-'A']=j;
if(flag)break;
}
printf(" %c",flag?'Y':'N');
}
cout<<endl;
}
}