AC自动机
AC自动机,全称Aho-Corasick自动机。如果没记错的话好像就是前缀自动机。
其实AC自动机就是KMP上树的产物。理解了KMP,那AC自动机应该也是很好理解的。
与KMP类似,AC自动机也是扔一个字符走一步。当前状态始终只有一个,每次如何走都是确定的,换句话说AC自动机是一种确定型有限状态自动机(DFA)。
进行模式匹配是AC自动机的基本应用。如果稍加拓展一下,就可以知道在AC自动机上走k步就相当于产生了一个长为k、只包含给定字符集的字符串。借助这个性质,可以在AC自动机上DP来解决一些字符串有关问题。
注意一般应用的时候都会把节点不存在的儿子指向跳fail之后的结果,这样就可以在匹配时省掉fail,代码简洁的同时还能提速。
来点例题:
1. COGS1913 AC自动机
一道AC自动机模板题,没啥好说的。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
void insert(const char*,int);
void getfail();
void match();
void inc(int);
int n,ch[maxn][]={{}},val[maxn]={},fail[maxn]={},last[maxn]={},q[maxn],head=,tail=,cnt=,a[];
char s[][],c;
int main(){
#define MINE
#ifdef MINE
freopen("ACautomata.in","r",stdin);
freopen("ACautomata.out","w",stdout);
#endif
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%s",s[i]);
insert(s[i],i);
}
getfail();
match();
for(int i=;i<=n;i++)printf("%s %d\n",s[i],a[i]);
#ifndef MINE
printf("\n-------------------------DONE-------------------------\n");
for(;;);
#endif
return ;
}
void insert(const char *c,int i){
int x=;
while(*c){
if(!ch[x][*c-'a'])ch[x][*c-'a']=++cnt;
x=ch[x][*c-'a'];
c++;
}
val[x]=i;
}
void getfail(){
int x,y;
for(int c=;c<;c++)if(ch[][c])q[tail++]=ch[][c];
while(head!=tail){
x=q[head++];
for(int c=;c<;c++){
if(ch[x][c]){
q[tail++]=ch[x][c];
y=fail[x];
while(y&&!ch[y][c])y=fail[y];
fail[ch[x][c]]=ch[y][c];
last[ch[x][c]]=val[fail[ch[x][c]]]?fail[ch[x][c]]:last[fail[ch[x][c]]];
}
else ch[x][c]=ch[fail[x]][c];
}
}
}
void match(){
char c;
int x=;
for(;;){
do c=getchar();while((c<'a'||c>'z')&&c!=EOF);
if(c==EOF)return;
x=ch[x][c-'a'];
if(val[x])inc(x);
else if(last[x])inc(last[x]);
}
}
void inc(int x){
if(!x)return;
a[val[x]]++;
inc(last[x]);
}
2. POI2000 病毒
对模式串建AC自动机,问题就转化为了在AC自动机上是否存在一个不经过任何危险节点(单词节点或者存在last的节点)且无限长的路径。换句话说,就是问AC自动机上有没有环。
然后直接上dfs找环即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
void insert(const char*,int=);
void init();
void dfs(int);
int n,ch[maxn][]={{}},val[maxn]={},fail[maxn]={},last[maxn]={},q[maxn],cnt=,head=,tail=;
bool vis[maxn]={false},ins[maxn]={false},ok=false;
char c[maxn];
int main(){
#define MINE
#ifdef MINE
freopen("wir.in","r",stdin);
freopen("wir.out","w",stdout);
#endif
scanf("%d",&n);
for(int i=;i<n;i++){
scanf("%s",c);
insert(c);
}
init();
dfs();
if(!n||ok)printf("TAK");
else printf("NIE");
#ifndef MINE
printf("\n-------------------------DONE-------------------------\n");
for(;;);
#endif
return ;
}
void insert(const char* c,int x){
while(*c){
if(!ch[x][*c-''])ch[x][*c-'']=++cnt;
x=ch[x][*c-''];
c++;
}
val[x]++;
}
void init(){
int x,y;
for(int c=;c<;c++)if(ch[][c])q[tail++]=ch[][c];
while(head!=tail){
x=q[head++];
for(int c=;c<;c++){
if(ch[x][c]){
q[tail++]=ch[x][c];
y=fail[x];
while(y&&!ch[y][c])y=fail[y];
fail[ch[x][c]]=ch[y][c];
last[ch[x][c]]=val[ch[y][c]]?ch[y][c]:last[ch[y][c]];
}
else ch[x][c]=ch[fail[x]][c];
}
}
}
void dfs(int x){
if(val[x]||last[x])return;
if(ins[x]){
ok=true;
return;
}
if(vis[x])return;
vis[x]=ins[x]=true;
for(int c=;c<;c++){
if(ch[x][c]){
dfs(ch[x][c]);
if(ok)return;
}
}
ins[x]=false;
}
3. COGS2248 情书
问你是否n个模式串都在文本串中出现过,AC自动机模板题。
话说string瞎搞居然比AC自动机快……
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
void insert(const char*,int);
void getfail();
void match(const char*);
void inc(int);
int n,ch[maxn][]={{}},fail[maxn]={},last[maxn]={},val[maxn]={},q[maxn],head=,tail=,cnt=,a[];
char s[],c;
bool ok;
int main(){
#define MINE
#ifdef MINE
freopen("lettera.in","r",stdin);
freopen("lettera.out","w",stdout);
#endif
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%*[^a-z]");
scanf("%[a-z]",s);
insert(s,i);
}
getfail();
for(;;){
scanf("%*[^a-z]");
if(scanf("%[a-z]",s)!=)break;
fill_n(a+,n,);
match(s);
ok=true;
for(int i=;i<=n;i++)if(!a[i]){
ok=false;
break;
}
printf(ok?"Yes\n":"No\n");
}
#ifndef MINE
printf("\n-------------------------DONE-------------------------\n");
for(;;);
#endif
return ;
}
void insert(const char *c,int i){
int x=;
while(*c){
if(!ch[x][*c-'a'])ch[x][*c-'a']=++cnt;
x=ch[x][*c-'a'];
c++;
}
val[x]=i;
}
void getfail(){
int x,y;
for(int c=;c<;c++)if(ch[][c])q[tail++]=ch[][c];
while(head!=tail){
x=q[head++];
for(int c=;c<;c++){
if(ch[x][c]){
q[tail++]=ch[x][c];
y=fail[x];
while(y&&!ch[y][c])y=fail[y];
fail[ch[x][c]]=ch[y][c];
last[ch[x][c]]=val[fail[ch[x][c]]]?fail[ch[x][c]]:last[fail[ch[x][c]]];
}
else ch[x][c]=ch[fail[x]][c];
}
}
}
void match(const char *c){
int x=;
while(*c&&*c!='$'){
x=ch[x][*c-'a'];
if(val[x])inc(x);
else if(last[x])inc(last[x]);
c++;
}
}
void inc(int x){
if(!x)return;
a[val[x]]++;
inc(last[x]);
}
其实例题还有很多,只不过限于时间(zi ji tai cai)没做,在此贴上大概思路:
1. [SCOI 2012] 喵星球上的点名
大概思路肯定是建AC自动机然后模式匹配。但是有一个问题,模式串太多,暴力匹配肯定会T。
考虑每个节点的last,显然它们构成了一棵树。因此可以对last树做树上差分,最后一遍dfs求出每个模式串出现的次数。至于每次有多少模式串出现,直接用节点的深度就行了。
2. 文本生成器(08年俞华程论文题)
感觉并不用减法原理就可以写……(不过不一定正确,如果有发现不对的话请告诉我)
先建AC自动机,然后就是一个AC自动机上的DP。
令f[i][j][0/1]为从i出发再走k步的方案数,第三维0表示没有出现过模式串,1表示出现过了。
转移方程:
f[i][j][0]=sum{f[ch[i][c]][j-1][0]}
f[i][j][1]=sum{f[ch[i][c]][j-1][1]+f[ch[i][c]][j-1][0]}
边界:
f[0][0][0]=1,其余j=0情况均为0。
然后,显然这是一个线性递推式,而状态又不多,因此可以用矩阵快速幂优化。构造一个转移矩阵,然后乘乘乘就好了。
尽头和开端,总有一个在等你。
AC自动机的更多相关文章
- 基于trie树做一个ac自动机
基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...
- AC自动机-算法详解
What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...
- python爬虫学习(11) —— 也写个AC自动机
0. 写在前面 本文记录了一个AC自动机的诞生! 之前看过有人用C++写过AC自动机,也有用C#写的,还有一个用nodejs写的.. C# 逆袭--自制日刷千题的AC自动机攻克HDU OJ HDU 自 ...
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]
3172: [Tjoi2013]单词 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 3198 Solved: 1532[Submit][Status ...
- BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]
1212: [HNOI2004]L语言 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1367 Solved: 598[Submit][Status ...
- [AC自动机]【学习笔记】
Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)To ...
- AC自动机 HDU 3065
大概就是裸的AC自动机了 #include<stdio.h> #include<algorithm> #include<string.h> #include< ...
- AC自动机 HDU 2896
n个字串 m个母串 字串在母串中出现几次 #include<stdio.h> #include<algorithm> #include<string.h> #inc ...
- 【BZOJ-3881】Divljak AC自动机fail树 + 树链剖分+ 树状数组 + DFS序
3881: [Coci2015]Divljak Time Limit: 20 Sec Memory Limit: 768 MBSubmit: 508 Solved: 158[Submit][Sta ...
随机推荐
- 数据表格 - DataGrid - 查询
toolbar头部工具栏 <script type="text/javascript"> $(function () { $("#datagrid" ...
- python脚步管理工具supervisor=3.3.0的安装、使用。基于linux系统。
一.安装相关包 sudo apt-get install python-pip #python的安装包的工具 sudo apt-get install python-dev # ...
- Andrew N.G的机器学习公开课学习笔记(一):机器学习的动机与应用
机器学习由对于人工智能的研究而来,是一个综合性和应用性学科,可以用来解决计算机视觉/生物学/机器人和日常语言等各个领域的问题,机器学习的目的是让计算机具有像人类的学习能力,这样做是因为我们发现,计算机 ...
- ffmpeg.exe dos下怎么用 放在哪里
系统:windows 7 1.先看dos界面,win7下这里输入cmd, 看路径 2.把下载的ffmpeg.exe复制到这个路径下 3.这就可以用命令了 1.mp4说明这个文件是跟ffmpeg.ex ...
- dockerRegistry搭建
docker registry安装: 官方仓库下载registry pull镜像: fu@ubuntu:~$ sudo docker pull registry 运行镜像 : sudo ...
- HtmlUnit初探
HtmlUnit是一个用java实现的浏览器,是一个无界面的浏览器(headless browser),跟phatomJS好像是同一类事物. HtmlUnit基于apache httpClient,而 ...
- 解决Centos/Redhat,命令不存在
[root@26 ~]# lsb_release #不存在-bash: lsb_release: command not found [root@26 ~] ...
- CSS编写技巧
1.尽量少的使用全局的重置代码 全局重置代码:*{margin:0; padding:0;}对于熟悉CSS的人来说并不陌生,并且有很多人的第一句CSS代码就是这句.它可以避免不同浏览器的默认间距不同而 ...
- php 字符串和数字比较一些问题
本文章来给大家介绍关于php 字符串和数字比较一些问题,因为数字与字符在php中是不同的数据类型,所以在比较时可能会有很多的问题. ,1,2等等,其中0标示成功,其他表示不同的错误代码.程序通过 if ...
- iOS 适配https(AFNetworking3.0为例)
众所周知,苹果有言,从2017年开始,将屏蔽http的资源,强推https楼主正好近日将http转为https,给还没动手的朋友分享一二 1.准备证书 首先找后台要一个证书(SSL证书,一般你跟后台说 ...