伟大的 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; }

__EOF__

本文作者F x o r G
本文链接https://www.cnblogs.com/xugangfan/p/16878105.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   FxorG  阅读(73)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示