AC自动机——学习笔记
来源:互联网 发布:淘宝助理批量修改价格 编辑:程序博客网 时间:2024/06/16 07:07
什么是自动机
是一种数学模型,大概就是由一堆状态和状态转移规则等东西构成,能与外界交换信息,并改变动作。
这个是理论上的东西,了解就行,对AC自动机的理解没有大影响。
什么是AC自动机?
通俗的讲就是在Trie上做kmp,处理多模式串匹配问题。
Trie 的每个结点就是一个状态,根结点是初始状态。
AC自动机的行为被定义为一下3个函数:
1. next函数 ch(q,a):返回从当前状态q走值为a的边后所到达的状态。如果根节点没有值为a的边,那么ch(0,a)=0,(就是说:当读入不匹配的字符时,自动机保持在初始状态)
如果从结点q出发有一条边值为a到达结点v,那么 ch(q,a)=v,否则ch(q,a)=Φ。
2. fail函数 fail(q):返回从状态q(q!=root)匹配错误时要转移到的状态
记L(q)表示Trie上q节点对于的字符串,
如果L(v)是L(q)的一个后缀,且是最长的后缀,则fail(q)=v(kmp的思想)。
fail(q)总是能返回一个状态,因为L(root)=Φ是任何模式串的前缀
3. output函数out(q):输出在状态q时,所有匹配的模式串。
上面的描述比较形式化。简单来说:
先把所有待匹配串插入到一颗Trie中。
对于每个节点,我们需要额外计算一些信息:
fail指针:如上所述,类似kmp中的next表。
cnt : 若这个位置是某个模式串的结尾,就cnt++。
last指针:指向沿着当前节点的fail指针往回走,第一个遇到的cnt>0的节点。是用于output的。
匹配
假设我们已经构造出了这些信息。考虑如何进行匹配,大概流程是这样的:
根据目标串一个一个字符在Trie上走,
每走到一个节点,执行一次output(顺着last指针往后累加答案)。走到下个节点时,如果ch(p,s[i])不为空则直接走过去,否则沿着fail指针走,直到匹配成功或者到初始状态(还是kmp)
注意到“沿着fail指针走,直到匹配成功或者到初始状态”这个过程较繁琐,而且只有ch(p,s[i])为空时才会执行,就想到可以优化一下,直接把ch(p,s[i])指到要跳到的节点。
代码如下:
int Find(P_node p){ // 即output int res=0; for(;p!=root;p=p->last) res+=p->cnt, p->cnt=0; return res; } int Query(char* s){ // 匹配 int res=0, len=strlen(s); P_node p=root; for(int i=0;i<=len-1;i++) p=p->ch[s[i]-'a'], res+=Find(p); return res;}
构造
回到如何构造这些信息,显然都可以一次对Trie的bfs遍历实现,具体对于p的某个儿子v:
v->fail=p->fail->ch[i];v->last=(v->fail->cnt?v->fail:v->fail->last);
要注意的是root的直接儿子的fail和last都指向root。
理解定义后还是比较显然的。
模板题PDU2222代码:
#include<cstdio>#include<cstring>#include<queue>#include<algorithm>using namespace std;struct node{ int cnt; node *fail,*last; node* ch[27];} base[500000], *len, nil, *null=&nil, *root;typedef node* P_node;P_node newnode(node* son=NULL){ len->cnt=0; len->fail=son; for(int i=0;i<=25;i++) len->ch[i]=son; return len++;}void Insert(P_node &p,char *now){ if(p==null) p=newnode(null); if((*now)=='\000'){ p->cnt++; return; } Insert(p->ch[(*now)-'a'],now+1);}queue<P_node> que;void build(){ root->fail=root->last=root; for(int i=0;i<=25;i++) if(root->ch[i]!=null){ P_node v=root->ch[i]; que.push(v); v->fail=v->last=root; } else root->ch[i]=root; while(!que.empty()){ P_node p=que.front(); que.pop(); for(int i=0;i<=25;i++) if(p->ch[i]!=null){ P_node v=p->ch[i]; que.push(v); v->fail=p->fail->ch[i]; v->last=(v->fail->cnt?v->fail:v->fail->last); } else p->ch[i]=p->fail->ch[i]; }}int Find(P_node p){ int res=0; for(;p!=root;p=p->last) res+=p->cnt, p->cnt=0; return res; } int Query(char* s){ int res=0, len=strlen(s); P_node p=root; for(int i=0;i<=len-1;i++) p=p->ch[s[i]-'a'], res+=Find(p); return res;}int _test,n,m;char s[1000005];int main(){ freopen("ac.in","r",stdin); freopen("ac.out","w",stdout); scanf("%d",&_test); while(_test--){ len=base; root=newnode(null); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%s",s), Insert(root,s); build(); scanf("%s",s); printf("%d\n",Query(s)); } return 0;}
- AC自动机——学习笔记
- AC自动机学习笔记
- AC自动机学习笔记
- 学习笔记:AC自动机
- AC自动机 学习笔记
- AC自动机学习笔记
- AC自动机跟随Kuangbing学习笔记
- |算法讨论|AC自动机 学习笔记
- AC自动机——讲解
- 算法导论—AC自动机
- AC自动机算法笔记
- AC自动机学习
- AC自动机学习记录
- AC自动机学习小记
- AC自动机的学习
- ac自动机学习
- AC自动机学习
- AC自动机学习小记
- c++派生类内存分布图
- Linux 安装器添加安全功能的必要性
- Codeforces Round #403 Div. 2 B. The Meeting Place Cannot Be Changed(二分)
- 创建和使用线程
- 二,性能测试
- AC自动机——学习笔记
- 一个菜鸟安装虚拟机的小结
- 4 数字加密 (15分)——一维数组
- XML文件编写自动提醒设置
- opencv-android笔记1:android studio 2.3 + opencv-android-sdk 实现 camera预览
- Javascript中三个常见问题(2)
- (HDU)2072
- C++之模板函数、类模板
- 爬虫编写经验