传送门
题解:
显然是一个裸的广义SAM。问题在于求right集合的大小。
我们知道广义SAM求right集合大小可以在建立完广义SAM后暴跳fail更新,也可以直接用set启发式合并。
随便写写就好了,这种东西就是怎么开心怎么玩。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
cs int N=1e5+7;
int n,m;
char s[N];int len;
namespace SAM{
cs int N=::N<<1|1;
std::set<int> S[N];
int son[N][26],fa[N],len[N],now=1;
inline int work(int p,int c){
int nq=++now,q=son[p][c];
memcpy(son[nq],son[q],sizeof son[q]);
len[nq]=len[p]+1;fa[nq]=fa[q];fa[q]=nq;
for(;p&&son[p][c]==q;p=fa[p])son[p][c]=nq;
return nq;
}
inline int push_back(int p,int c){
if(son[p][c]){
if(len[son[p][c]]==len[p]+1)return son[p][c];
else return work(p,c);
}
else {
int cur=++now;
len[cur]=len[p]+1;
for(;p&&!son[p][c];p=fa[p])son[p][c]=cur;
if(!p)fa[cur]=1;
else if(len[son[p][c]]==len[p]+1)fa[cur]=son[p][c];
else fa[cur]=work(p,c);
return cur;
}
}
int bin[N],nd[N],siz[N];
inline void build(){
for(int re i=1;i<=now;++i)++bin[len[i]];
for(int re i=1;i<=now;++i)bin[i]+=bin[i-1];
for(int re i=1;i<=now;++i)nd[bin[len[i]]--]=i;
for(int re i=now;i;--i){
int v=nd[i],u=fa[v];siz[v]=S[v].size();
if(u){
if(S[v].size()>S[u].size())swap(S[v],S[u]);
for(auto t:S[v])S[u].insert(t);
}
S[v].clear();
}
}
}
inline void solve(){
using SAM::siz;
using SAM::son;
int u=1;scanf("%s",s+1);len=strlen(s+1);
for(int re i=1;i<=len;++i){
u=son[u][s[i]-'a'];
if(!u){std::cout<<"0\n";return ;}
}
std::cout<<siz[u]<<"\n";
}
signed main(){
#ifdef zxyoi
freopen("in.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
for(int re i=1;i<=n;++i){
scanf("%s",s+1);len=strlen(s+1);
for(int re j=1,p=1;j<=len;++j){
p=SAM::push_back(p,s[j]-'a');
SAM::S[p].insert(i);
}
}
SAM::build();
while(m--)solve();
return 0;
}