AC自动机基础入门
AC自动机Aho-Corasick automation 用途 字符串的匹配问题 多串的匹配问题 例如给几个单词 acbs,asf,dsef, 再给出一个 很长的文章,acbsdfgeasf 问在这个文章中,总共出现了多少个单词,或者是单词出现的总次数。 实现的原理 形象的说:KMP+trie树(字典树) 思路:在一棵trie树上面做Kmp,每个节点都有个像Kmp一样匹配失败时的指针(fail指针),匹配失败时按照失败指针指向的节点继续匹配。 什么是trie树(字典树)? Trie树 有神马特点 有一个空的根节点。 对于一般的trie数是解决用英文字母组成文章。所以每个节点会有26个子节点(指针)。 构造的过程 开始的时候有一个root 过程 要在该树中插入一个单词she 过程 再加一个单词shr。 过程 再插入say和her等,这样一个trie树就搞定了 如何与kmp联系在一起? 关键是在trie树上 加了一种fail指针。 Fail指针的用途:使当前字符失配时跳转到具有最长公共前后缀的字符继续匹配,如同 KMP算法一样, AC自动机在匹配时如果当前字符匹配失败,那么利用fail指针进行跳转。由此可知如果跳转,跳转后的串的前缀,必为跳转前的模式串的后缀并且跳转的新位置的深度(匹配字符个数)一定小于跳之前的节点。所以我们可以利用 bfs在 Trie上面进行 fail指针的求解。 在字符串失配的时候确定转移的节点。 先看到底是什么样的 这只显示了e的fail指针。 例如匹配文章:sher 如何高效的构造前缀指针 1 2 3 6 4 5 7 8 A A A B B B B ROOT1号节点的前缀指针指向0号节点 接下来按照BFS顺序构造每个节点的前缀指针 定义虚拟节点0号节点,0号节点的所有连出的字边都连向ROOT1号节点 0 A、B 2号节点: 父亲是1号节点,连接字符为A,查找父亲的前缀指针0号节点,是否有通过A连接的儿子。 有!于是2号节点的前缀指针指向1号节点 3号节点: 父亲是1号节点,连接字符为B,查找父亲的前缀指针0号节点,是否有通过B连接的儿子。 有!于是3号节点的前缀指针指向1号节点 4号节点: 父亲是2号节点,连接字符为B,查找父亲的前缀指针1号节点,是否有通过B连接的儿子。 有!于是4号节点的前缀指针指向3号节点 5号节点: 父亲是3号节点,连接字符为A,查找父亲的前缀指针1号节点,是否有通过A连接的儿子。 有!于是5号节点的前缀指针指向2号节点 8号节点: 父亲是7号节点,连接字符为B,查找父亲的前缀指针5号节点,是否有通过B连接的儿子。 没有,继续查找5号节点的前缀指针2号节点是否有通过B连接的儿子。 有!于是8号节点的前缀指针指向4号节点 7号节点: 父亲是4号节点,连接字符为A,查找父亲的前缀指针3号节点,是否有通过A连接的儿子。 有!于是7号节点的前缀指针指向5号节点 6号节点: 父亲是3号节点,连接字符为B,查找父亲的前缀指针1号节点,是否有通过B连接的儿子。 有!于是6号节点的前缀指针指向3号节点 至此,这棵树的前缀指针我们就设计完成了!! 对于一个插入了n个模式串的单词前缀树构造其前缀指针的时间复杂度为:O(∑len(i)) AC自动机算法分为3步: 1.构造一棵Trie树 2.构造fail指针 3.模式匹配过程 构造fail指针的原理: 根据父亲节点的fail指针来构造子节点的fail指针。 hdu2222 5 //单词数 she //单词 he say shr her Yasherhs//文章 问出现单词数的和 代码实现 struct node { int next[26]; int fail; int count; void init() { memset(next, -1, sizeof(next)); fail = 0; count = 0; } }s[500005];