AC 自动机
写给自己看的。
\(\mathrm {AC}\) 自动机用在处理多个待匹配串的字符串匹配问题。
首先建出字符集 \(T\) 的 \(\mathrm {Trie}\) 树 \(T_t\),接着考虑类似 \(\mathrm {kmp}\) 的思想,令一个节点 \(u\) 的失配指针 \(fail_u\) 表示从根到 \(u\) 组成的字符串 \(S\) 的最长后缀,满足是 \(T\) 的某一个前缀。
考虑如何建出 \(fail\),令节点 \(u\) 下一步的的字符是 \(c\) 的转移为 \(\mathrm {trans(u, c)}\),那么转移如下:
-
\(u\) 存在儿子 \(v\),且 \(s(u, v) = c\),则 \(\mathrm {trans}(u, c) = v, fail_v = u\)
-
\(\mathrm{Otherwise:}\) \(\mathrm{trans(u, c) = trans(fail_u, c)}\)
不难发现可以用一个宽搜实现。
匹配直接上就行。code
接下来我们让 \(u\) 向 \(fail_u\) 连一条边。不难发现这构成了一棵树 \(T_f\),我们称其为 \(\mathrm{fail}\) 树。通过定义可以知道,对于一个节点 \(u\),它的父亲节点所代表的字符串都是节点 \(u\) 的后缀。
所以就有了一个经典 \(\rm Trick\):对于一个节点 \(u\) 所代表的字符串 \(S_u\),它在 \(T\) 中出现的次数就是它作为某一个字符串的前缀 的后缀 的次数。换句话说,假设令 \(S_0 = \{e_i | e_i \in anc_{T_f}(u)\}\),\(S_1 = \{e_i, e_i \in sub_{T_t}(u)\}\),出现的次数就是 \(|S_0 \bigcap S_1|\)。