Trie & AC 自动机
本篇为学习笔记。
Trie 字典树
先放张来自 OI Wiki 的图——
可以看到每个节点都有一个编号,每个编号都代表了一个字符串。
节点之间的有向边边上有字符 \((u,v)=c\),即 \(u\) 所代表的字符串在结尾添加 \(c\) 后为 \(v\) 代表的字符串。
一般根节点编号为 \(0\)。
对于每个节点,需要存当前节点代表的字符串是否存在(存在的个数)。
AC 自动机
前置知识:自动机。
AC 自动机常用于解决多模式串匹配的任务。
众所周知 KMP 虽然是线性的,但是一次只能匹配一个模式串。而 AC 自动机一次可以匹配多个模式串。
考虑:
- 对模式串建立 Trie。
- 从根节点出发,每新增一个字符,就走相应的边。
- 如果不存在相应边,回到根节点。
- 如果走到的节点存在,则匹配成功。
但是这样会少匹配。比如如果有模式串 \(\texttt{abe},\texttt{bc}\),文本串 \(\texttt{abcde}\),那么会在 \(\texttt{ab\color{red}{e}}\) 处失配,回到根节点,错失了 \(\texttt{bc}\)。
AC 自动机利用一个 fail 指针来辅助多模式串的匹配。
什么是 fail 指针?
它是每个节点都有的指针,指向当前(节点所代表的)字符串的(Trie 上已存在的)最长真后缀。
举个例子,上图中 \(12\) 代表 \(\texttt{caa}\),它指向的是 \(5:\texttt{aa}\)。又如 \(10\to 4,13\to 6,14\to 7\cdots\)。
那么 fail 指针有什么用?
依然以模式串 \(\texttt{abe},\texttt{bc}\),文本串 \(\texttt{abcde}\) 为例,那么在 \(\texttt{ab\color{red}{e}}\) 处失配后,会跳到代表 \(\texttt{b}\) 的节点,尝试继续匹配。
那么 fail 指针怎么建?
考虑到 fail 指针指向的是最长真后缀,故指向的节点深度一定更高。bfs 解决:
- 树根先入队
- 队列非空时,取出队首节点 \(u\),遍历每个字母 \(c\),记 \(u[c]\) 为对应的儿子
- 若 \(u[c]\) 存在,将 \(u[c].\text{fail}\gets u.\text{fail}[c]\)
- 反之,\(u[c]\gets u.\text{fail}[c]\)