广义后缀自动机 (General Suffix Automaton)
定义
广义后缀自动机是后缀自动机和 Trie 结合的产物, 通俗地讲就是在 Trie 上建立后缀自动机. 后缀自动机维护的是单个字符串的信息, 而广义后缀自动机维护的则是多个字符串的信息.
如果你还不会后缀自动机或 Trie, 请移步这两篇博客:
做法
问: 建立 GSAM 有几步.
答: 有 \(3\) 步
-
先建立一棵边上存字符, 而不是节点存字符的 Trie, 作为广义后缀自动机的主链.
-
在 Trie 上跑一边 BFS, 保存 Trie 的 BFS 序.
-
按 BFS 序像建立后缀自动机一样一个一个加入当前节点入边的字符 (因为字符存在边上)
模板: Luogu-P6139
给 \(n\) 个字符串, 求它们本质不同的子串数量. (\(\displaystyle{\sum_{i = 1}^n}len_{s_i} \leq 10^6\), \(n \leq 4*10^5\))
根据前面的结论, 一个 \(Endpos\) 等价类中字符串连续递增 (由前一个字符串前面增加字符得到). 又因为一个 \(Link\) 链上一个等价类中最短字符串长度等于它 \(Link\) 最长的字符串长度加 \(1\), 所以一个 \(Link\) 链上所有节点的等价类的所有字符串也是连续递增的.
根据后缀自动机节点不重不漏表达字符串子串这个性质, 就可以将每个节点的字符串数相加得到答案.
具体细节如 Trie 的建立, BFS 的实现, SAM 的构建等都不再赘述, 其实 GSAM 就是 SAM 的一个特殊应用, 没有太多新东西, 思维难度也不高.
代码
#include
#include
#include
#include
#include
#include
#include
#include
注意事项
-
构造 GSAM 时, 遇到节点分裂的情况, 新的转移要等批量重定向时一起修改, 否则, \(A\) 的 \(To[c]\) 提前从 \(C \rightarrow c\) 修改为 \(A \rightarrow c\), 会影响 while
跳出条件的判定 (A->To[c] == C_c
), 导致无论什么情况, 都无法将其他节点重定向. 我这个蠢货因为这个从 \(10:00\) 调到 \(3:00\), 写了全套对拍程序, 找来 std 对拍, 人工精简出锅数据, 手玩出锅数据, 在纸上构造 GSAM. 最后发现程序过程输出的 \(To\) 连接诡异, 发现是重定向有问题. 这时才恍然大悟, 无论什么数据, 我的重定向代码不会被执行哪怕一遍, 这样也能得 \(35'\), 这题数据是真的水.
-
本题统计答案时, \(Ans\) 需开 long long
, 否则 \(75'\)
最后放出我找到并简化 (原输出 \(300+\)) 的卡掉了我 \(35'\) 程序的数据, 如果输出 \(24\), 说明你也是重定向的问题.
P6139_0.in
2
fcceded
fce
P6139_0.out
25