伟大的 chy,请赐予我力量&chy 自动机的 SAM 做法
https://www.luogu.com.cn/problem/U260001
给定一组文本串,再每次给你一个询问串,求文本串的前缀集合中有多少串的 border 集合包含该询问串。
考虑 border 也许会往 kmp 方面入手,因为 kmp 能求出来一个前缀的最长 border。但注意到,这个包含意味着可能不是最长的,而不是最长的你对应位置又要发生变化,显然无法从最长 border 转移。
然而,你观察到一个串的 border 集合包含询问串,显然询问串是该串的前缀,且该串又作为后缀出现。
于是考虑对每个文本串的前缀求出其 endpos,其 endpos 位置的前缀的 border 集合一定包含该询问前缀。
至于去重,你考虑用 trie 对于出现过的前缀就不赋值,但询问的时候还是要考虑其贡献的。
对答案的记录直接挂在 trie 上即可。
#include <bits/stdc++.h>
//#define int long long
#define pb push_back
using namespace std;
const int N=(int)(5e6+5);
struct edge {
int nex,to;
}e[N*3];
char s[N];
bool fl[N];
int las,tot=1,m,q,n,val[N*3],len[N*3],fa[N*3],cch[26][N],ctot=1,ch[26][N],sz[N*3],hea[N*3],cnt;
void add_edge(int x,int y) {
e[++cnt].nex=hea[x]; e[cnt].to=y; hea[x]=cnt;
}
//int ins(int pre,int c,bool ok) {
// if(ch[c][pre]&&len[ch[c][pre]]==len[pre]+1) return ch[c][pre];
// int x=++tot; bool chk=ch[c][pre]; len[x]=len[pre]+1;
// if(ok) sz[x]=1;
// for(;pre&&!ch[c][pre];pre=fa[pre]) ch[c][pre]=x;
// int y=ch[c][pre];
// if(!pre) {
// fa[x]=1; return x;
// } else if(len[y]==len[pre]+1) {
// fa[x]=y; return x;
// }
// int p=++tot; len[p]=len[pre]+1;
// fa[p]=fa[y]; fa[x]=fa[y]=p;
// for(int i=0;i<26;i++) ch[i][p]=ch[i][y];
// for(;pre&&ch[c][pre]==y;pre=fa[pre]) ch[c][pre]=p;
// if(chk) return p;
// return x;
//}
void ins(int c,bool ok) {
int x=++tot,pre=las; las=x; len[x]=len[pre]+1;
sz[x]=ok;
for(;pre&&!ch[c][pre];pre=fa[pre]) ch[c][pre]=x;
int y=ch[c][pre];
if(!pre) return fa[x]=1,void();
else if(len[y]==len[pre]+1) return fa[x]=y,void();
else {
int p=++tot; len[p]=len[pre]+1;
fa[p]=fa[y]; fa[y]=fa[x]=p;
for(int i=0;i<26;i++) ch[i][p]=ch[i][y];
for(;pre&&ch[c][pre]==y;pre=fa[pre]) ch[c][pre]=p;
}
}
void dfs(int x) {
// cout<<x<<" "<<sz[x]<<'\n';
for(int i=hea[x];i;i=e[i].nex) {
int y=e[i].to;
dfs(y); sz[x]+=sz[y];
}
}
signed main() {
cin.tie(0); ios::sync_with_stdio(false);
cin>>m>>q;
while(m--) {
cin>>s+1; n=strlen(s+1);
las=tot=1; int p=1;
for(int i=1;i<=n;i++) {
bool ok=0; int c=s[i]-'a';
if(!cch[c][p]) cch[c][p]=++ctot,ok=1;
p=cch[c][p];
ins(c,ok);
// las=ins(las,s[i]-'a',ok);
fl[i]=ok;
}
for(int i=2;i<=tot;i++) add_edge(fa[i],i);
dfs(1);
int tp=1; p=1;
for(int i=1;i<=n;i++) {
int c=s[i]-'a';
tp=cch[c][tp]; p=ch[c][p];
if(fl[i]) {
if(sz[p]>=1) val[tp]+=sz[p]-1;
} else {
if(sz[p]) val[tp]+=sz[p];
}
}
for(int i=1;i<=n;i++) fl[i]=0;
for(int i=1;i<=tot;i++) {
sz[i]=hea[i]=fa[i]=len[i]=0;
for(int j=0;j<26;j++) ch[j][i]=0;
}
cnt=0; las=1; tot=1;
}
while(q--) {
cin>>s+1; n=strlen(s+1);
int p=1; bool ok=1;
for(int i=1;i<=n;i++) {
int c=s[i]-'a';
if(!cch[c][p]) {
ok=0; break ;
}
p=cch[c][p];
}
if(!ok) {
cout<<"0\n"; continue ;
}
cout<<val[p]<<'\n';
}
return 0;
}