题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3277
https://www.lydsy.com/JudgeOnline/problem.php?id=3473
学习的博客:https://www.cnblogs.com/HocRiser/p/9580478.html
广义后缀自动机有两种写法,这里写的是 trie 树的那种。
大意就是每个串从自动机的根开始走,
1.如果存在 q = go[p][w] ,且 l [q] == l [p]+1 , 那么直接把 q 看作这次插入了的点,下次令 p = q ,续着往后插入;
2.如果存在 q = go[p][w] ,且 l [q] != l [p]+1 , 那么分出一个 nq 来,把 nq 看作这次插入了的点,下次令 p = nq ,续着往后插入;
3.如果不存在 q = go[p][w] ,就新建一个 np ;然后就是一个串的后缀自动机插入了;下次令 p = np 。
要计算后缀自动机上的每个点代表的那些子串在所有串里出现了多少次,记为 ct[ ] ;
在做一个串的插入的时候,考虑让一些位置的 ct[ ] ++ ;每插入一个字符,就跳 fa ,给所有 fa 的 ct[ ] ++ ;但如果已经被当前字符串的之前字符弄得 ct[ ] 加一过了,就不用再加了;
不要边建自动机边做 ct[ ] ++ ;因为那时的 parent 树还不是所有串的。
然后拓扑排序一下,从根到叶子做 dp ,自己节点可以贡献的合法子串个数 ans[ ] 就是 ans[ fa ] 再加上 “ 自己出现了 >= k 次?l [cr] - l [fa] : 0 ” 。
两道题用同样的代码即可。
#include<cstdio> #include<cstring> #include<string>// #include<algorithm> #define ll long long using namespace std; int Mx(int a,int b){return a>b?a:b;} const int N=1e5+5,M=N<<1,K=27;//<<1 int n,k,go[M][K],l[M],fa[M],tot=1; int tx[M],q[M],ans[M],ct[M],vis[M]; int cz(int p,int w) { int q=go[p][w],nq=++tot;l[nq]=l[p]+1; fa[nq]=fa[q];fa[q]=nq; memcpy(go[nq],go[q],sizeof go[q]); for(;p&&go[p][w]==q;p=fa[p])go[p][w]=nq; return nq; } int ins(int p,int w) { if(go[p][w]) { int q=go[p][w]; if(l[q]==l[p]+1)return go[p][w]; else return cz(p,w);////// } else { int np=++tot;l[np]=l[p]+1; for(;p&&!go[p][w];p=fa[p])go[p][w]=np; if(!p)fa[np]=1; else { int q=go[p][w]; if(l[q]==l[p]+1)fa[np]=q; else fa[np]=cz(p,w); } return np; } } void Rsort(int mxn) { for(int i=1;i<=tot;i++)tx[l[i]]++; for(int i=1;i<=mxn;i++)tx[i]+=tx[i-1]; // for(int i=tot;i;i--)q[tx[l[i]]--]=i; for(int i=1;i<=tot;i++)q[tx[l[i]]--]=i; } string ch[N]; int len[N]; int main() { scanf("%d%d",&n,&k); char tp[N]; int mxn=0; for(int i=1,d;i<=n;i++) { scanf("%s",tp);len[i]=strlen(tp);mxn=Mx(mxn,len[i]); ch[i]=tp; } for(int i=1;i<=n;i++) for(int lm=len[i],pr=1,j=0;j<lm;j++) pr=ins(pr,ch[i][j]-'a'+1); for(int i=1;i<=n;i++) for(int lm=len[i],cr=1,j=0;j<lm;j++) { cr=go[cr][ch[i][j]-'a'+1]; for(int p=cr;p&&vis[p]!=i;p=fa[p]) ct[p]++,vis[p]=i; } Rsort(mxn); for(int i=2,d;i<=tot;i++) {d=q[i];ans[d]=ans[fa[d]]+(ct[d]>=k?l[d]-l[fa[d]]:0);}// for(int i=1;i<=n;i++) { ll prn=0; for(int lm=len[i],cr=1,j=0;j<lm;j++) { cr=go[cr][ch[i][j]-'a'+1]; prn+=ans[cr]; } printf("%lld ",prn); } puts("");return 0; }
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2780
和上一道题一样。
#include<cstdio> #include<cstring> #include<algorithm> #include<string> using namespace std; int const xn=2e5+5; const int N=1e4+5,M=2e5+5,N2=4e5+5,K=27; int n,go[M][K],fa[M],l[M],ct[M],tot=1; string s[N];int len[N],vis[M]; int cz(int p,int w) { int q=go[p][w],nq=++tot; l[nq]=l[p]+1;/////////l[p]+1 not l[q]+1!!!!! fa[nq]=fa[q];fa[q]=nq; memcpy(go[nq],go[q],sizeof go[q]); for(;p&&go[p][w]==q;p=fa[p])go[p][w]=nq; return nq; } int ins(int p,int w) { if(go[p][w]) { int q=go[p][w]; if(l[q]==l[p]+1)return q; else return cz(p,w); } else { int np=++tot;l[np]=l[p]+1; for(;p&&!go[p][w];p=fa[p])go[p][w]=np; if(!p)fa[np]=1; else { int q=go[p][w]; if(l[q]==l[p]+1)fa[np]=q; else fa[np]=cz(p,w); } return np; } } char ch[N2]; int Q; int main() { scanf("%d%d",&n,&Q); for(int i=1;i<=n;i++) { scanf("%s",ch);len[i]=strlen(ch); s[i]=(string)ch; for(int pr=1,j=0,lm=len[i];j<lm;j++) pr=ins(pr,ch[j]-'a'+1); } for(int i=1;i<=n;i++) for(int cr=1,j=0,lm=len[i];j<lm;j++) { cr=go[cr][s[i][j]-'a'+1]; for(int p=cr;p>1&&vis[p]!=i;p=fa[p]) ct[p]++,vis[p]=i; } while(Q--) { scanf("%s",ch);int lm=strlen(ch),cr=1; for(int j=0;j<lm&&cr;j++) cr=go[cr][ch[j]-'a'+1]; printf("%d ",ct[cr]); } return 0; }