[AC自动机]【学习笔记】
摘抄、修改课件:
- AC自动机就是在Trie上进行类似KMP的过程,可以进行多模板匹配
1.如何得到多个匹配模板的fail函数?(建立AC自动机)
KMP是从左到右进行,那么在Trie上进行时,需要从根结点开始按BFS的顺序进行。
BFS到一个节点时,求它的孩子的fail函数
求x->ch[i]->fail时,先令now=x->fail。
若存在now->ch[i],就使
x->ch[i]->fail = now->ch[i]
否则令now=now->fail,继续这个过程。
如果now=root后仍不存在now->ch[i],
就使x->ch[i]->fail = root- 2.如何匹配一段文本?
初始在root,依次枚举B中每一个字符。
假设现在在now,当前字符为c。
若存在now->ch[c],到达now->ch[c]。
否则令now=now->fail,继续这个过程。
如果now=root后仍不存在now->ch[c],就停留在root点。- AC自动机中,fail指针仍然会形成一个树结构,称之为fail树。(要将指向翻转)
Fail树的一个性质是,某个结点所对应的字符串肯定是其后代结点所对应的字符串的后缀。
- 也就是说,B的前i个字符在AC自动机上跑完之后,如果到达点x,不仅x对应的字符串是B[1,i]的一个后缀,所有x在fail树中的祖先结点对应的字符串都是B[1,i]的一个后缀。
- 3.如何求A在B中出现了多少次?
如果B的前i个字符在AC自动机中跑完之后到达点x,A对应的点是y。那么只要在fail树中y是x的祖先(x失配会向y方向走)结点,A就是B[1,i]的后缀。
B在AC自动机中每跑完一个字符,都在当前点上记录访问次数+1。
A的对应点y在fail树中的子树中所有结点的访问次数之和就是A在B中出现的次数。
- 4.一个小优化:(链接)
- 我们可以看出,fail是用来寻找失配时走到的位置的,走一个点fail的他的ch[i]一定是没有的。那么我们为什么不用这些ch指针直接指向它的fail的ch[i]呢,可以发现这样操作之后,每个点的ch[i]直接指向了原本沿着失配边不停走的最终结果,这样的话,匹配时每次用ch指针的对象即可,不用一直沿fail边走看看这个点有没有ch[i]了。这个被称作Trie图。
- 更简洁的说,t[u].ch[i] 就是从节点u走ch[i]应该到的位置
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e6+,M=5e5+;
int n;
struct node{
int ch[],f,val;
}t[M];
int root,sz;
char s[N];
void ins(char s[]){
int u=root,n=strlen(s+);
for(int i=;i<=n;i++){
int c=s[i]-'a';
if(!t[u].ch[c]) t[u].ch[c]=++sz;
u=t[u].ch[c];
}
t[u].val++;
}
int q[N],head=,tail=;
void getAC(){
head=tail=;
q[tail++]=root;
while(head!=tail){
int u=q[head++];
for(int i=;i<;i++){
int v=t[u].ch[i];
if(v){
if(u==root) t[v].f=root;
else{
int now=t[u].f;
while(now!=root&&!t[now].ch[i]) now=t[now].f;
if(t[now].ch[i]) now=t[now].ch[i];
t[v].f=now;
}
q[tail++]=v;
}
}
}
}
int ans,vis[N];
void AC(char s[]){
int now=root,n=strlen(s+);
for(int i=;i<=n;i++){
int c=s[i]-'a';
while(now!=root&&!t[now].ch[c]) now=t[now].f;
if(t[now].ch[c]){
now=t[now].ch[c];
for(int _=now;_!=root&&!vis[_];_=t[_].f)
ans+=t[_].val,vis[_]=;
}
}
}
void init(){
memset(t,,sizeof(t));
memset(vis,,sizeof(vis));
root=sz=ans=;
}
int main(){
//freopen("in.txt","r",stdin);
int T; scanf("%d",&T);
while(T--){
scanf("%d",&n);
init();
for(int i=;i<=n;i++) scanf("%s",s+),ins(s);
getAC();
scanf("%s",s+);
AC(s);
printf("%d\n",ans);
}
}
加优化 343ms
注意getAC()通过把root的所有有的孩子加入队列减少了一种边界讨论(即因为t[0].fail=0造成的那个),因为root的孩子的fail就是0(root)
注意引用v
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e6+,M=5e5+;
int n;
struct node{
int ch[],f,val;
}t[M];
int sz;
char s[N];
void ins(char s[]){
int u=,n=strlen(s+);
for(int i=;i<=n;i++){
int c=s[i]-'a';
if(!t[u].ch[c]) t[u].ch[c]=++sz;
u=t[u].ch[c];
}
t[u].val++;
}
int q[N],head=,tail=;
void getAC(){
head=tail=;
for(int i=;i<;i++)
if(t[].ch[i]) q[tail++]=t[].ch[i];
while(head!=tail){
int u=q[head++];
for(int i=;i<;i++){
int &v=t[u].ch[i];
if(!v) {v=t[t[u].f].ch[i];continue;}//meiyou ch,zhijie tiaodao fail
t[v].f=t[t[u].f].ch[i];
q[tail++]=v;
}
}
}
int ans,vis[N];
void AC(char s[]){
int now=,n=strlen(s+);
for(int i=;i<=n;i++){
int c=s[i]-'a';
now=t[now].ch[c];
for(int _=now;_!=&&!vis[_];_=t[_].f)
ans+=t[_].val,vis[_]=;
}
}
void init(){
memset(t,,sizeof(t));
memset(vis,,sizeof(vis));
sz=ans=;
}
int main(){
//freopen("in.txt","r",stdin);
int T; scanf("%d",&T);
while(T--){
scanf("%d",&n);
init();
for(int i=;i<=n;i++) scanf("%s",s+),ins(s);
getAC();
scanf("%s",s+);
AC(s);
printf("%d\n",ans);
}
}
[AC自动机]【学习笔记】的更多相关文章
- AC自动机学习笔记-2(Trie图&&last优化)
我是连月更都做不到的蒟蒻博主QwQ 考虑到我太菜了,考完noip就要退役了,所以我决定还是把博客的倒数第二篇博客给写了,也算是填了一个坑吧.(最后一篇?当然是悲怆のnoip退役记啦QAQ) 所以我们今 ...
- AC自动机板子题/AC自动机学习笔记!
想知道484每个萌新oier在最初知道AC自动机的时候都会理解为自动AC稽什么的,,,反正我记得我当初刚知道这个东西的时候,我以为是什么神仙东西,,,(好趴虽然确实是个对菜菜灵巧比较难理解的神仙知识点 ...
- [AC自动机][学习笔记]
用途 AC自动机适用于一类用多个子串在模板串中匹配的字符串问题. 也就是说先给出一个模板串,然后给出一些子串.要求有多少个子串在这个模板串中出现过. KMP与trie树 其实AC自动机就是KMP与tr ...
- AC自动机学习笔记-1(怎么造一台AC自动机?)
月更博主又来送温暖啦QwQ 今天我们学习的算法是AC自动机.AC自动机是解决字符串多模匹配问题的利器,而且代码也十分好打=w= 在这一篇博客里,我将讲解AC自动机是什么,以及怎么构建一个最朴素的AC自 ...
- AC 自动机学习笔记
虽然 NOIp 原地爆炸了,目前进入 AFO 状态,但感觉省选还是要冲一把,所以现在又来开始颓字符串辣 首先先复习一个很早很早就学过但忘记的算法--自动 AC AC自动机. AC 自动机能够在 \(\ ...
- AC自动机学习笔记
AC自动机 ----多个模板的字符串匹配 字典树Trie加上失配边构成 插入操作:ac.insert(p[i],i);构造失配函数:ac.getFail();计算文本串T中每个模板串的匹配数:ac.f ...
- 【AC自动机】【字符串】【字典树】AC自动机 学习笔记
blog:www.wjyyy.top AC自动机是一种毒瘤的方便的多模式串匹配算法.基于字典树,用到了类似KMP的思维. AC自动机与KMP不同的是,AC自动机可以同时匹配多个模式串, ...
- AC自动机学习
今天包括这一周开始学习AC自动机了,有点晚,但我感觉努努力还来得及.4月份还得认认真真攻图论,加油! 为2个月后的邀请赛及省赛.东北赛做准备. 推荐AC自动机学习地址:http://www.cppbl ...
- 后缀自动机&回文自动机学习笔记
在学了一天其实是边学边摆之后我终于大概$get$后缀自动机了,,,就很感动,于是时隔多年我终于决定再写篇学习笔记辽$QwQ$ $umm$和$FFT$学习笔记一样,这是一篇单纯的$gql$的知识总结博, ...
- AC自动机学习小结
AC自动机 简要说明 \(AC\) 自动机,全称 \(Aho-Corasick\ automaton\) ,是一种有限状态自动机,应用于多模式串匹配.在 \(OI\) 中通常搭配 \(dp\) 食用. ...
随机推荐
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(8)-MVC与EasyUI DataGrid 分页
系列目录 前言 为了符合后面更新后的重构系统,文章于2016-11-1日重写 EasyUI Datagrid在加载的时候会提交一些分页的信息到后台,我们需要根据这些信息来进行数据分页再次返回到前台 实 ...
- YYModel 源码解读(二)之YYClassInfo.h (3)
前边3篇介绍了YYClassinfo 文件的组成单元,算是功能的分割,按照业务的设计思想来说,方向应该是相反的 由此引申出我们在设计api的思想其实和项目管理是很类似的----- 一些题外话 1.目的 ...
- 时间复杂度分别为 O(n)和 O(1)的删除单链表结点的方法
有一个单链表,提供了头指针和一个结点指针,设计一个函数,在 O(1)时间内删除该结点指针指向的结点. 众所周知,链表无法随机存储,只能从头到尾去遍历整个链表,遇到目标节点之后删除之,这是最常规的思路和 ...
- GitHub更新自己Fork的项目
转自:http://www.tuicool.com/articles/MzMJre github上有个功能叫fork,可以将别人的工程复制到自己账号下.这个功能很方便,但其有一个缺点是:当源项目更新后 ...
- .Net 序列化(去除默认命名空间,添加编码)
1.序列化注意事项 (1).Net 序列化是基于对象的.所以只有实例字段呗序列化.静态字段不在序列化之中. (2)枚举永远是可序列化的. 2.XML序列化时去除默认命名空间xmlns:xsd和xmln ...
- Sqlserver调用api
虽然使用sqlserver去调用服务接口的情况比较少,但也可以去了解下对应的使用情况 一.首先要开启组件的配置 sp_configure ; GO RECONFIGURE; GO sp_configu ...
- ef
现阶段使用回溯 entityframework作为.net平台自己的一个orm的框架,之前在项目中也有使用,主要采用了table和model first的方式,此两种感觉使用上也是大同小异.在项目中经 ...
- Android 手机卫士1--实现splash页面
1.minSdkVersion.targetSdkVersion.maxSdkVersion.target API level四个数值到底有什么区别? minSdkVersion, maxSdkVer ...
- 大朋展翅 html5上传图片(三)一解决部分手机拍相册批量上传图片转向问题
在经过前面的改进之后本来以为已经没有问题了,但经过我们神通广大的测试的测试,发现相册中的图片在上传时也会发生转向问题.既然前面都解决了拍照转向的问题,那么相册中图片的上传也容易解决.修改一下需要旋转图 ...
- dialog 模块化窗口
xDialog 方法 说明 参数 modal(opts) 模块化弹窗 opts={ title:'标题' , width : '宽度(400)', height : '高度(300)', button ...