数据结构与算法:Aho‐Corasick自动机

Aho‐Corasick自动机 阅读报告

作者: Wenretium
注意: 文章修改自本人《数据结构与算法》课程作业,图文全部为本人原创,仅供参考。欢迎一起学习探讨٩( ‘ω’ )و 能帮助到你的话请点个赞夸夸再走哦

一、算法简介

Aho-Corasick算法是多模式匹配中的经典算法,该算法通过构造AC自动机,达到用空间换取时间的效果。
假定有 m m m 个模式串 S 1 , S 2 , … S m S_{1},S_{2},…S_{m} S1​,S2​,…Sm​ 以及一个文本串 T T T,要找出所有模式串在 T T T 中的出现(occurrences),AC自动机能够将时间复杂度减少至 O ( ∣ S 1 ∣ + … ∣ S m ∣ + ∣ T ∣ + t ) O(|S_{1}|+…|S_{m}|+|T|+t) O(∣S1​∣+…∣Sm​∣+∣T∣+t)。

二、算法思想

AC自动机在构造的过程中缓存了不同的状态,包含“按字符转移成功(非模式串的结尾)”、“按字符转移成功(是模式串的结尾)”、“按字符转移失败”三种情况下的跳转与输出情况。
在匹配过程中,按照文本顺序,使字符逐个进入AC自动机,发生相应状态转移,直至文本串结束,找出所有出现的模式串。
数据结构与算法:Aho‐Corasick自动机

三、算法实现

AC自动机由转向函数(goto)、失配函数(failure)、输出函数(output)三个部分组成,分别对应匹配过程中可能出现的三种状态。

1. 建立Trie

以模式串 { 00000 , 01 , 11 } \left\{00000, 01, 11\right\} {00000,01,11} 为例,如图3-1,其中,蓝色有向边代表起点是终点的“父亲”(即起点对应字符串增加一个字符可得终点对应字符串),红色标记结点代表一个模式串的结尾。在建立Trie的过程中,goto(蓝边)和output(红点)函数已构建完毕。
数据结构与算法:Aho‐Corasick自动机

2. 为Trie添加失配指针

如图3-2,橙色有向边代表失配指针。
1)原理
失配指针中,终点对应字符串是起点对应字符串的最大严格后缀。类比KMP算法,当匹配失败时,发生状态的跳转,从上一个字符串的匹配模式转移到另一个字符串的匹配中来,且后者为前者的最大严格后缀,省去了已比较部分(即后一字符串本身)的重复工作。

2)方法

先用bfs对Trie进行层序遍历。易知失配指针一定指向上层结点,因为一个字符串的最大严格后缀长度一定小于其本身。所以在这步,用层序遍历逐层向下构造failure路径。注意,Trie中第二层结点的失配路径直接指向root节点。
然后对每个遍历到的结点进行处理。假设有一个字母为M的结点,构造此结点的失配指针,则从M的父节点出发,沿着失配指针一直走,直到走到一个结点,它的儿子中也有字母为M的结点。此时,将结点M的失配指针指向这个字母为M的结点(如结点C)。如果一直走到了root节点,也没有找到,则把结点M的失配指针指向root(如结点B)。
数据结构与算法:Aho‐Corasick自动机

此时,AC自动机构建完毕。

3. 根据AC自动机,匹配文本串

这里以伪代码的形式展示整个匹配过程。
数据结构与算法:Aho‐Corasick自动机

四、算法分析

构造Trie ( goto和output ) : O ( ∣ S 1 ∣ + … ∣ S m ∣ ) O(|S_{1}|+…|S_{m}|) O(∣S1​∣+…∣Sm​∣),构造failure: O ( 结 点 数 ) = O ( ∣ S 1 ∣ + … ∣ S m ∣ ) O(结点数) = O(|S_{1}|+…|S_{m}|) O(结点数)=O(∣S1​∣+…∣Sm​∣),匹配过程: O ( ∣ T ∣ + t ) O(|T|+t) O(∣T∣+t)。
所以,AC算法时间复杂度为 O ( ∣ S 1 ∣ + … ∣ S m ∣ + ∣ T ∣ + t ) O(|S_{1}|+…|S_{m}|+|T|+t) O(∣S1​∣+…∣Sm​∣+∣T∣+t)。

五、算法运用

题目简介: 有若干二进制病毒代码段,如某段代码中不存在任何一段病毒代码,则称这段代码是安全的。试求与给定病毒代码段相对应的无限长的安全二进制代码。(
原题 P2444 [POI2000]病毒

1. 解题思想

在用AC自动机匹配文本串时,目的是尽可能命中模式串。而本题目要求生成一个无限长的字符串,其中不命中任何一个模式串,称为无限长的安全代码。
可将寻找安全代码的过程视作匹配的逆过程。将AC自动机中父结点到儿子的边、失配指针都视作有向边,整个Trie看作一个图。只要使图中存在一个环,匹配指针在环中不断循环,且此环中不含危险结点,就能相应找到(生成)一个无限长的安全代码。

2. 解题步骤

与前面的步骤一样,先构造AC自动机。而与上面不同的是,Trie中结点分为危险结点和普通结点,其中危险结点包含output结点(在此情景下全为Trie的叶子节点)和包含output串的其他字符串的尾结点。在给结点添加失配指针时,如果此结点失配指针指向的结点为output结点,则此结点也为危险结点,因为此结点对应字符串包含了危险串。
然后用dfs递归在Trie中寻找闭环,过程中注意避开危险结点,且此环一定能够从root节点到达。如果存在这样一个环,则取环中被访问到的结点,生成无限长的安全代码;如果不存在这样一个环,则安全代码不存在。
如图3-2,模式串 { 00000 , 01 , 11 } \left\{00000, 01, 11\right\} {00000,01,11} 不存在这样一个环,则不能找到无限长的安全代码。如图5-1,模式串 { 00000 , 011 , 11 } \left\{00000, 011, 11\right\} {00000,011,11} 能找到这样一个环,则存在无限长的安全代码为010101…。

数据结构与算法:Aho‐Corasick自动机

上一篇:【python】Leetcode每日一题-前缀树(Trie)


下一篇:死磕以太坊源码分析之state