SAM(后缀自动机)总结
“写sam是肯定会去写的,这样才学的了字符串,后缀数组又不会用 >ω<,
sam套上数据结构的感觉就像回家一样!
里面又能剖分又能线段树合并,调试又好调,我爱死这种写法了 !qwq”
SAM是一个DFA,它存储了某字符串的所有子串信息。
待更。
博主水平不行,尽量在退役前多更些。
插入字符:
1 void extend(int id,int&now) 2 { 3 int p=now; 4 if(ch[p][id] && len[ch[p][id]]==len[p]+1) 5 { 6 now=ch[p][id]; 7 return; 8 } 9 int np=++tot; 10 len[np]=len[p]+1; 11 while(p && !ch[p][id]) 12 { 13 ch[p][id]=np; 14 p=fa[p]; 15 } 16 if(!p) 17 { 18 fa[np]=1; 19 } 20 else 21 { 22 int q=ch[p][id]; 23 if(len[q]==len[p]+1) 24 { 25 fa[np]=q; 26 } 27 else 28 { 29 int nq=++tot; 30 len[nq]=len[p]+1; 31 fa[nq]=fa[q]; 32 for(int i=0;i<26;i++) 33 ch[nq][i]=ch[q][i]; 34 fa[q]=fa[np]=nq; 35 while(p && ch[p][id]==q) 36 { 37 ch[p][id]=nq; 38 p=fa[p]; 39 } 40 } 41 } 42 now=np; 43 }
套路:
每条路径与子串一一对应。
$len_i$:该节点最长串的长度
$Parent Tree$上的子树和:串的出现次数
$len_i - len_{fa_i}$:该节点代表的串的数目
部分题目选:
(1)[SAM入门题][NOI2018]你的名字
线段树合并维护$right$集合
(2)[SAM入门题][CTSC2012]熟悉的文章
$SAM$只是用来预处理的,求答案还要靠二分+单队$DP$
(3)[SAM入门题][bzoj2555]Substring
$LCT$对$Parent Tree$结构的维护
(4)[bzoj3277]字符串
广义后缀自动机的应用
暴力搞复杂度大概是根号的,不会证明。
(5)[bzoj5084]HASHIT
带撤销后缀自动机,随机数据随便艹。
upd:BFS建树形后缀树以后再进行DFS+树链求并统计答案就行了。
(6)[USACO]牛奶模式
后缀自动机的基本原理与使用
(7)[洛谷元旦赛]WD与数列
维护$right$集合的好题(似乎还要在parent树上跑启发式合并计算贡献,我并不会)
现在可能会了。
(8)[FZOJ2258]封印
校内题目,没连接
大概就是你知道$Parent$树是干啥的,形态如何,就能直接切掉。
(9)[TJOI2016]字符串
考虑求出 sam 上一个串 p 在 [l,r] 里出现的次数,这个可以通过数一数 p 的 right 集合里有多少在 $[l+len_p-1,r]$ 中出现。
lcp 可以二分,倍增可以从区间 locate 子串,那么这个题就 $O(n\log^2 n)$ 了。
upd:话说为啥没见过把状态设在sam节点上的DP题?
upd:CF700E,已经单独更新
(10)[十二省联考]字符串问题
题目意思大概是描述给你一个连边关系,求 DAG 上最长路。
直接暴力构图可以 $n^2$ ,用 hash 判断。
如果考虑建后缀树,那么在一个节点上有短->长的后缀关系,另外父子关系也是后缀关系,拆点据此连边即可。
只需要实现对区间 locate 子串即可,时间复杂度 $O(n\log n)$。