一 点睛
AC 自动机是 KMP 算法和 Trie 树的结合,是经典的多模匹配算法。首先将多个模式串构建一棵字典树,然后在字典树上添加失配指针,失配指针相当于 KMP 算法中的 next 函数(匹配失败时的回退位置),最后将主串在 Trie 树上进行模式匹配。
AC自动机算法分为3步。
1 构建一棵字典树。
2 构建 AC 自动机。
3 进行模式匹配。
二 构建字典树
插入一个字符串时,从前往后遍历字符串,从字典树的根开始判断当前要插入的字符节点是否已存在,若已存在,则沿该分支遍历下一个字符;若不存在,则创建一个新节点表示这个字符。继续遍历其他字符,直到字符串处理完毕。
假设有单词 she、he、his、hers,构建一棵字典树。
三 构建 AC 自动机
KMP 算法中的 next 函数(回退函数或者fail函数)。next 函数表示 S[i] 与 T[j] 不等时 j 应该回退的位置。
AC 自动机的失配指针有同样的功能,匹配失败时,跳转到当前节点的失配指针所指向的节点,再次进行匹配操作。AC自动机可以实现多模式匹配,归功于失配指针(fail指针)。
给字典树的每个节点添加失配指针,AC 自动机就构造完成了。
AC 自动机的失配指针指向的节点代表的字符串是当前节点代表的字符串的最长后缀。
对于当前节点 t,t 的的子指针 t->ch[i] 分为两种情况:
• t->ch[i] 不为空:t->ch[i] 的失配指针指向 t->fail->ch[i]。
• t->ch[i] 为空:t->ch[i] 指向 t->fail->ch[i]。
算法步骤如下:
1 树根入队。
2 若队列不为空,则取队头元素 t 并出队,访问 t 的每一个子节点 t->ch[i]:
t->ch[i] 不为空:t->ch[i] 的失配指针指向 t->fail->ch[i],并将 t->ch[i] 入队。
t->ch[i] 为空:t->ch[i] 指向 t->fail->ch[i]。
3 队空时,算法结束。
四 模式匹配
模式匹配指从树根开始处理模式串的每个字符,沿着当前字符的 fail 指针,一直遍历到 u->count=-1 为止,在遍历过程中累加这些节点的 u->count,累加后将节点标记为 u->count=-1,避免重复统计。u->count 大于或等于 1 的节点都是可以匹配的节点。
例如,在字符串 shers 中包含了几个单词?
五 性能分析
给定 k 个单词和一段包含 n 个字符的文章,求有多少个单词在文章里出现过。
若使用 KMP 算法,则每个模式串 Ti 都要与主串 S 进行一次匹配,总时间复杂度为 O(n*k+m),其中 n 为主串 S 的长度,m 为模式串 T1,T2,…Tk 的长度之和,k 为模式串的个数。而采用 AC 自动机,时间复杂度只需 O(n+m)。