【算法笔记】Aho-Corasick 算法(AC自动机) 小结
来源:互联网 发布:字体管家mac版 编辑:程序博客网 时间:2024/05/01 19:35
参考:
《算法竞赛入门经典-训练指南》
http://codeforces.com/blog/entry/14854
kuang bin blog
加上不存在的边,并压缩冗余转移
这个改进《训练指南》中说过, 本文的目的是总结并实现一个自用的模板。。
AC自动机每个节点有一个 fail 指针, 作用与KMP中相同。
假如我们的节点是这样的
// link 就是 fail 指针const int MaxNode = 500000 + 50; const int CharSet = 26; int go[MaxNode][CharSet], link[MaxNode]; int val[MaxNode]; // 附加信息,例如判断是否为一个单词的结尾 int nodes;
在AC自动机中输入一个新的字符c,转移时会先沿着link指针走,遇到 go[c] not equal 0 的节点 或者到了根就会停下。
中间走的这一段都不存在c转移,在构造的时候已经知道这个事实了,所以可以把这一段多跑的路给压缩掉。
把原来不存在的转移都补上
就再也没有while了~~
void build() { queue<int> q; q.push(0); while ( !q.empty() ) { int u = q.front(); q.pop(); int fa = link[u]; for (int i = 0; i < CharSet; ++i) { int v = go[u][i]; if ( v ) { link[v] = u ? go[fa][i] : 0; q.push(v); } else { go[u][i] = go[fa][i]; } } } }
init(); ac_insert("hers"); ac_insert("she"); ac_insert("he"); ac_insert("s"); get_draw(root, ""); ac_construct(); match("she"); return 0;
改进后fail指针的指向
一开始写的是指针形式的,也是预先分配内存
struct Node { Node *go[CharSet], *link; int count;};
不过指针的开销貌似会更大? 虽然看起来是一样的。。
hdu 2222 一直 MLE
所以实际的板子改成了开数组的
用last指针进一步提高转移效率
用last指针表示沿着link走,遇到的第一个为某个单词结尾的节点。
这样在match的时候,沿着 last 走而不是 link,遇到的都是单词节点
void build() { queue<int> q; q.push(0); while ( !q.empty() ) { int u = q.front(); q.pop(); int fa = link[u]; for (int i = 0; i < CharSet; ++i) { int v = go[u][i]; if ( v ) { link[v] = u ? go[fa][i] : 0; last[v] = val[link[v]] ? link[v] : last[link[v]]; q.push(v); } else { go[u][i] = go[fa][i]; } } } }
我的模板
namespace Trie { const int MaxNode = 500000 + 50; const int CharSet = 26; int go[MaxNode][CharSet], link[MaxNode], last[MaxNode]; int val[MaxNode]; int nodes; void init() { nodes = 1; memset(go[0], 0, sizeof(go[0])); } int allocate() { memset(go[nodes], 0, sizeof(go[nodes])); val[nodes] = 0; last[nodes] = 0; link[nodes ++] = 0; return nodes - 1; } void add( const char* s ) { int cur = 0; for (; *s; ++ s) { int id = *s - 'a'; if ( !go[cur][id] ) { go[cur][id] = allocate(); } cur = go[cur][id]; } val[cur] += 1; } int match(const char* s) { int cur = 0, ret = 0; for (; *s; ++s) { cur = go[cur][*s-'a']; int tmp = cur; if ( val[tmp] ) { ret += val[tmp], val[tmp] = 0; } while ( last[tmp] ) { tmp = last[tmp]; if ( val[tmp] ) { ret += val[tmp], val[tmp] = 0; } } } return ret; } void build() { queue<int> q; q.push(0); while ( !q.empty() ) { int u = q.front(); q.pop(); int fa = link[u]; for (int i = 0; i < CharSet; ++i) { int v = go[u][i]; if ( v ) { link[v] = u ? go[fa][i] : 0; last[v] = val[link[v]] ? link[v] : last[link[v]]; q.push(v); } else { go[u][i] = go[fa][i]; } } } }}
构造不包含模式的串
当 自动机 构造好后,我们有了 r 个状态。那么就存在一个 r x r的矩阵 T,T(i,j) 表示状态 i 转移到状态 j 的方法数
T 可以这样构造
// 输入 (当前状态,输入) // 返回转移到的状态, -1 表示无效转移int check(int st, int input) { st= go[st][input]; // 如果当前节点或其后缀是模式串,则为无效转移 if ( tag[st] || last[st] ) return -1; return st;}Mat T(r); // r 为状态总数memset(T.m, 0, sizeof(T.m));for (int i = 0; i < n; ++i) { for (int j = 0; j < CharSet; ++j) { int st = check(i, j); if ( st != -1 ) T.m[i][st] += 1; }}
称不包含模式的串为合法串
矩阵
例题
POJ 2778 DNA Sequence
详解可以看这篇
0 0
- 【算法笔记】Aho-Corasick 算法(AC自动机) 小结
- Aho-Corasick自动机算法(简称AC自动机
- Aho-Corasick自动机算法(AC算法解读)
- 【AC自动机】:Aho-Corasick算法的实现
- 浅谈AC自动机(Aho-Corasick automaton算法)
- Aho-Corasick算法—Trie图(AC自动机)
- Aho-Corasick 多模式匹配算法、AC自动机详解
- Aho-Corasick 多模式匹配算法、AC自动机详解
- AC自动机(Aho-Corasick automaton)
- 深入理解Aho-Corasick自动机算法
- 多模式串匹配之AC自动机算法(Aho-Corasick算法)简介与C语言程序实现源码参考
- 多模式串匹配之AC自动机算法(Aho-Corasick算法)简介与C语言程序实现源码参考
- Aho-Corasick automation,AC 自动机
- Aho-Corasick算法(转)
- Aho-Corasick算法学习
- Aho-Corasick算法学习
- Aho-Corasick算法学习
- Aho-Corasick算法
- hdoj 1513 Palindrome 【LCS】【滚动数组】
- bitmap
- 考试
- C#里怎样把一个DataTable的数据追加进数据库里的某个表
- js中的简单排序
- 【算法笔记】Aho-Corasick 算法(AC自动机) 小结
- Android 编程下的 EditView 阻止软键盘自动弹出__图片资源文件后缀不能为.ico
- 约瑟夫环问题 循环链表
- MapReduce的处理流程(一)
- 用过滤器解决全站中文数据提交乱码问题
- Binary Tree Inorder Traversal
- [ACM] HDU 1242 Rescue (优先队列)
- poj 3274 Gold Balanced Lineup
- JavaScript 知识点总结-3