AC自动机 trie
来源:互联网 发布:为知笔记安卓导出 编辑:程序博客网 时间:2024/05/16 04:33
AC 自动机就是在 trie 上做 KMP ,先构造所有字符串的一个 trie ,再添加失败边(failure links),失败边跟 KMP 里的“失败函数”是一样的道理。
trie树实际上是一个DFA(Deterministic finite automaton),通常用转移矩阵表示。行表示状态,列表示输入字符,(行, 列)位置表示转移状态。这种方式的查询效率很高,但由于稀疏的现象严重,空间利用效率很
低。也可以采用压缩的存储方式即链表来表示状态转移,但由于要线性查询,会造成效率低下。
这是一个统计词频的trie树实例(来自wiki)
#include <stdio.h>#include <stdlib.h>#include <string.h> #define TREE_WIDTH 256 #define WORDLENMAX 128 struct trie_node_st { int count; struct trie_node_st *next[TREE_WIDTH];}; static struct trie_node_st root={0, {NULL}}; static char *spaces=" \t\n/.\"\'()"; static intinsert(const char *word){ int i; struct trie_node_st *curr, *newnode; if (word[0]=='\0') { return 0; } curr = &root; for (i=0; ; ++i) { if (word[i] == '\0') { break; } if (curr->next[ word[i] ] == NULL) { newnode=(struct trie_node_st*)malloc(sizeof(struct trie_node_st)); memset(newnode, 0, sizeof(struct trie_node_st)); curr->next[ word[i] ] = newnode; } curr = curr->next[ word[i] ]; } curr->count ++; return 0;} static voidprintword(const char *str, int n){ printf("%s\t%d\n", str, n);} static intdo_travel(struct trie_node_st *rootp){ static char worddump[WORDLENMAX+1]; static int pos=0; int i; if (rootp == NULL) { return 0; } if (rootp->count) { worddump[pos]='\0'; printword(worddump, rootp->count); } for (i=0;i<TREE_WIDTH;++i) { worddump[pos++]=i; do_travel(rootp->next[i]); pos--; } return 0;} intmain(void){ char *linebuf=NULL, *line, *word; size_t bufsize=0; int ret; while (1) { ret=getline(&linebuf, &bufsize, stdin); if (ret==-1) { break; } line=linebuf; while (1) { word = strsep(&line, spaces); if (word==NULL) { break; } if (word[0]=='\0') { continue; } insert(word); } } /* free(linebuf); */ do_travel(&root); exit(0);}
构造 AC 自动机
添加失败边的算法:对每一个节点,找出它的最长的在 trie 中出现过的后缀。比如对 she ,先找 he ,再找 e ,如果都没有则失败到 root 。
但这种算法效率比较低,没有充分利用已经计算出来的信息。网上流传的是另外一种算法:比如要找 she 的失败边,可以假定其父节点,sh 的失败边已经求出,沿着父节
点的失败边走,如果某个节点有 e 的子节点,则把失败边指向该子节点。比如 she 的父节点 sh ,先走到 h ,假如 h 没有 e 的子节点的话,就继续检查 h 的失败边,即
root 。如果最后到 root 都没有,就把失败边置为 root 。
匹配
在 AC 自动机上匹配:从根开始,如果当前字符匹配,则移动指针。然后需要沿着失败边检查:看有没有哪个是结尾节点,是则匹配成功(网上有些代码是只有当前整个串匹配
成功才找,但实际上只要当前字符是一样的就要找)。比如如果用上面的 AC 自动机匹配 she ,匹配到 she 的时候,she 这个单词匹配成功,还要沿失败边检查,发现 he
和 e 也匹配成功。如果当前字符不匹配,则沿着失败边继续找,如果都没有匹配,则回到 root 。
1 const int kind = 26;
2 struct node{
3 node *fail; //失败指针
4 node *next[kind]; //Tire每个节点的个子节点(最多个字母)
5 int count; //是否为该单词的最后一个节点
6 node(){ //构造函数初始化
7 fail=NULL;
8 count=0;
9 memset(next,NULL,sizeof(next));
10 }
11 }*q[500001]; //队列,方便用于bfs构造失败指针
12 char keyword[51]; //输入的单词
13 char str[1000001]; //模式串
14 int head,tail; //队列的头尾指针
1 void insert(char *str,node *root){
2 node *p=root;
3 int i=0,index;
4 while(str[i]){
5 index=str[i]-'a';
6 if(p->next[index]==NULL) p->next[index]=new node();
7 p=p->next[index];
8 i++;
9 }
10 p->count++; //在单词的最后一个节点count+1,代表一个单词
11 }
2 int i;
3 root->fail=NULL;
4 q[head++]=root;
5 while(head!=tail){
6 node *temp=q[tail++];
7 node *p=NULL;
8 for(i=0;i<26;i++){
9 if(temp->next[i]!=NULL){
10 if(temp==root) temp->next[i]->fail=root;
11 else{
12 p=temp->fail;
13 while(p!=NULL){
14 if(p->next[i]!=NULL){
15 temp->next[i]->fail=p->next[i];
16 break;
17 }
18 p=p->fail;
19 }
20 if(p==NULL) temp->next[i]->fail=root;
21 }
22 q[head++]=temp->next[i];
23 }
24 }
25 }
26 }
1 int query(node *root){
2 int i=0,cnt=0,index,len=strlen(str);
3 node *p=root;
4 while(str[i]){
5 index=str[i]-'a';
6 while(p->next[index]==NULL && p!=root) p=p->fail;
7 p=p->next[index];
8 p=(p==NULL)?root:p;
9 node *temp=p;
10 while(temp!=root && temp->count!=-1){
11 cnt+=temp->count;
12 temp->count=-1;
13 temp=temp->fail;
14 }
15 i++;
16 }
17 return cnt;
18 }
- AC自动机和Trie
- AC自动机 trie
- Trie 图 AC自动机
- Trie树,AC自动机模版
- Trie、KMP、AC自动机小结
- kmp,trie树,ac自动机
- 【算法】AC自动机--Trie图
- AC自动机 (Trie图优化)
- hdu2222,AC自动机,trie图
- 关于Trie KMP AC自动机
- trie树+AC自动机板子
- HihoCoder1036[Trie图 ] AC自动机
- KMP&&trie树&&AC自动机
- AC自动机(Trie图)
- 字符串 KMP Trie AC自动机 后缀数组
- 从KMP算法,trie树再到AC自动机
- hihoCoder #1036 : Trie图 (AC自动机)
- hihoCoder 1036 Trie图(AC自动机)
- ffmpeg中264部分的内存管理机制
- Java Pojo转flex vo 工具类
- 雨中飘荡的回忆
- win7系统如何恢复或重装IE8浏览器呢?
- 【C++ Primer 学习笔记】chapter 11 泛型算法
- AC自动机 trie
- Windows 2008 R2 域服务器web方式修改域用户密码
- 数据结构-线性表-顺序表
- 浅谈最近的自身程序员的想法(2012.09.13)
- ffserver源码分析
- “未能恢复-无效的自变量”的解决方法
- asp.net 操作数据过大时,报错:对象的当前状态使该操作无效
- Java通用分页
- 2.14 求子数组之和的最大值