字符串匹配算法之Aho-Corasick

字符串匹配算法之Aho-Corasick_我思故我在_百度空间
字符串匹配算法之Aho-Corasick
2008-12-18 16:23

Aho-Corasick可以认为是KMP算法在多串查找的扩展。先把所有要查找的串放在一起建一棵trie树。然后从源串的各个位置开始的串到trie树里查找。这样每次查找的复杂度为O(m),m为最长的子串。总共要查n次,所以复杂度为O(nm)。这只能说是brute force算法在多串下的扩展。所以还要在trie树里添加一些转移,使得源串在匹配过程中不出现回退。假设当前节点为i,源串匹配到字符c,如果节点i不存在c字母对应的转移。这时候应该跳转到一个节点j,从trie树根节点到这个节点的字母组成的字符串,应该是从根节点组成的字符串的后缀,如果有多个这样的节点,则跳转到最短的一个。分析一下这个自动机在匹配时的复杂度。要匹配过程中,源串不回退,所以遍历源串的复杂度为O(n)。但是在匹配失败的时候,会出现跳转,可能要跳转很多次才可以匹配成功。但是注意一点,每次跳转使得深度至少减1,而深度至多跟源串匹配的长度相等,所以可以跳转的次数不会超过源串的长度,所以总的复杂度是O(n)。

要注意一个地方,后缀关系是可以传递的,a是b的后缀,b是c的后缀,则a是c的后缀。所以上面的表达式,最终是把一个串的后有后缀,从长到短串成了一个链表。

难的地方就是自动机的构造。为了便于分析,把建trie树跟增加跳转分成两个过程。假设现在已经建好trie树。要为一个节点增加跳转,最简单的方法是,把根结点到它一路上的字符串组成的字符串,求后缀,再到trie树里查找,因为是求最长的后缀,所以从长到短,逐一去查找,直至找到为止,找到的节点就是要跳转的节点。这种方法的复杂度很高。观察一下,假设在trie树里,i节点是j节点的父结点,j的跳转结点的父结点表示的串,也是i的后缀。反过来说,根据一个节点的父结点的跳转可以快速地找到这个结点的跳转。假设当前节点的父结点的跳转结点为k,下一个要匹配的字符为c,看k是否有c的跳转,如果没有,则k继续跳转。会否出现没有找到合法的跳转,而k又不能再跳转的情况呢?初始的时候,把所有节点的跳转都指向根结点,可以避免这种情况。

在实现的时候,因为跳转总是从深度大的结点跳到深度小的结点,所以对trie树作广度遍历。现在来分析一下构造的复杂度。trie树中结点的总数不会超过待查找的子串的长度和。但是每个结点的查找会进行多次跳转。分析一个子串它对应的一系列节点,每交跳转会使得深度减1,而这些节点的深点至多为子典的长度,所以一个子串对应的所有结点的构造费用之和,不会超过字串的长度,所以总的构造复杂度是跟字符长度和成正比的。


你可能感兴趣的:(字符串)