概述

ACAC自动机是以TrieTrie为结构基础,kmpkmp为思想基础建立的,主要用于多模式串匹配。

ACAC自动机上,所有的模式串构成一棵TrieTrie树,而且利用kmpkmp的思想,在TrieTrie上构造失配指针。

TrieTrie上的结点表示的是某个模式串的前缀,相当于一种状态,而TrieTrie上的边就相当于是状态的转移。

failfail指针

先把所有的模式串放到TrieTrie,举例如下:
AC自动机【学习笔记】
假如说现在要匹配的文本串是ABCDABCD,我们去树上匹配,会经过2,3,42,3,4号节点匹配到模式串ABCABC,然后就不能继续匹配。

如果接下来重新从根结点开始,复杂度会很高,我们可以借用kmpkmp的思想,跳到77去,77就是44的失配指针。

More officially,failfail指针指向 / 模式串的前缀中 / 匹配 / 当前状态的最长后缀。(断句要断好)
也就是说,ii的失配指针jj,满足root>jroot->jroot>iroot->i的一个后缀,而且是所有满足root>i=root>jxroot->i=root->j_x中最大的那一个jxj_x
99号点也满足条件,但是那里不是最长后缀,所以我们不跳到那里去。

下面是failfail的求法:
TrieTrie上当前的节点是uuuu的父亲是pptrie[p][c]=utrie[p][c]=u
假设深度小于uu的所有结点的failfail指针都已经求过。

1.trie[fail[p]][c]trie[fail[p]][c]存在,那么fail[u]=trie[fail[p]][c]fail[u]=trie[fail[p]][c]pp的最长后缀的位置在fail[p]fail[p],在fail[p]fail[p]的位置再加一个字符cc就一定uu的最长后缀的位置,因为只在一个确定的串后面加上一个字符而已。
2.如果trie[fail[p]][c]trie[fail[p]][c]不存在,那么就要一直跳failfail指针(反复横跳),找trie[fail[fail[p]]][c]trie[fail[fail[p]]][c],直到它存在,然后重复1.
3.如果真的不存在,那么把failfail指向根。

具体可以用bfsbfs实现(有假设深度小于uu的所有结点的failfail指针都已经求过) ,不过实现和刚才的思考过程是反的。
tr[u][c]tr[u][c]可以理解为字典树上的一条边,也可以理解为一种状态转移,表示uu加上一个字符cc达到的状态。
AC自动机【学习笔记】
代码实际上修改了TrieTrie的结构,但是使得匹配转移更加完善。它将 fail 指针跳转的路径做了压缩(就像并查集的路径压缩),使得本来需要跳很多次ailail针变成跳一次。

匹配函数

failfail是最难的部分,failfail理解之后求答案就水到渠成了吧。
AC自动机【学习笔记】

时间复杂度

AC自动机【学习笔记】
【参考:OIwiki】

相关文章: