失配指针
定义 fail 为广义的 $\pi$ 函数,即对于 $n$ 个字符串,$fail_{i,j}=\max\limits_{k=1}^n \pi(S_k\#S_{i,1\sim j})$。
上面那个是一种定义,但事实上,在 $\rm AC$ 自动机中,fail 是具有指向性的,它指向的是所有模式串中,前缀与当前字符串某个前缀的后缀匹配可以产生最大匹配的那个串,它与当前串匹配的最后一个结点。
构建 AC 自动机
首先对所有模式串建立 Trie 树,显然 Trie 树上的每一个结点都对应某个模式串的某个前缀。
然后将每个 fail 都指向匹配的结点,显而易见,所有的 fail 都指向一个祖先节点,这个性质后面要用到。
最后构建转移边 Auto,Auto 指向跳 fail 过程中存在字符 $c$ 的结点。
具体来说,可以以 $\rm BFS$ 序遍历 Trie 树,在遍历的同时更新答案,转移式如下:
$$\begin{cases}fail_u=Auto_{fail_{fa_u},c}\\Auto_{u,c}=Auto_{fail_u,c}\end{cases}
$$
建 $\rm AC$ 自动机复杂度为 $\Theta(|\Sigma|n)$。
文本串匹配
遍历文本串,直接按照转移边走即可,如果要统计每个模式串出现的次数,就将所有经过的点权值 $+1$,再在 fail 树上差分即可计算。
令模式串为 $S$,文本串为 $T$,总时间复杂度为 $\Theta(|\Sigma||S|+|T|)$。
例题:[HNOI2004]L语言
首先有一个显然的 $\Theta(|s|\sum|t|)$ 暴力,枚举文本串的转移点,显然其不能超过最大串长,转移式为:
$$f_i=\bigvee_{j=i-|s|}^{i-1}\{f_j\wedge[t_{j+1\sim i}\in s]\}
$$
这个式子的核心在于,$f_i=true$ 当且仅当它存在一个后缀,这个后缀是某个模式串,并且删去这个后缀以后的文本串仍合法。
于是考虑建立 $\rm AC$ 自动机,将文本串放在自动机上跑的时候记 $f_i$ 表示当前前缀是否合法。
存在如下 $\rm AC$ 自动机性质:如果 $v$ 可以从 $u$ 开始由跳 fail 得到,即 fail 树上 $v$ 是 $u$ 的祖先,那么 $v$ 代表的字符串是 $u$ 代表的字符串的一个后缀。
所以从 $u$ 开始遍历跳 fail 经过的结点,如果它代表的字符串长度为 $l$,且为某个模式串的结尾,则将 $g_{u,l}$ 记为 true。 由于 $|s|\leqslant 10$,所以只有当 $l\leqslant 10$ 的时候 $g_{u,l}$ 可能为 true,故直接状压成 $g_u$,其二进制下第 $l$ 位表示之前 $g_{u,l}$ 的值。
最后直接将文本串放在 $\rm AC$ 自动机上跑即可,记 $now_i$ 表示文本串中当前点前 $i$ 位的 $f$ 值为多少,易知 $i\leqslant 10$,故也可以状压成一个数 $now$。 易知 $f_i=true$ 当且仅当 $[now~or~g_u]=true$。
总时间复杂度 $\Theta(|\Sigma|n|s|^2+m|t|)$。