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自动机的更多相关文章

  1. 基于trie树做一个ac自动机

    基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...

  2. AC自动机-算法详解

    What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...

  3. python爬虫学习(11) —— 也写个AC自动机

    0. 写在前面 本文记录了一个AC自动机的诞生! 之前看过有人用C++写过AC自动机,也有用C#写的,还有一个用nodejs写的.. C# 逆袭--自制日刷千题的AC自动机攻克HDU OJ HDU 自 ...

  4. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Sta ...

  5. BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status ...

  6. BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]

    1212: [HNOI2004]L语言 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1367  Solved: 598[Submit][Status ...

  7. [AC自动机]【学习笔记】

    Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)To ...

  8. AC自动机 HDU 3065

    大概就是裸的AC自动机了 #include<stdio.h> #include<algorithm> #include<string.h> #include< ...

  9. AC自动机 HDU 2896

    n个字串 m个母串 字串在母串中出现几次 #include<stdio.h> #include<algorithm> #include<string.h> #inc ...

  10. 【BZOJ-3881】Divljak AC自动机fail树 + 树链剖分+ 树状数组 + DFS序

    3881: [Coci2015]Divljak Time Limit: 20 Sec  Memory Limit: 768 MBSubmit: 508  Solved: 158[Submit][Sta ...

随机推荐

  1. 数据表格 - DataGrid - 查询

    toolbar头部工具栏 <script type="text/javascript"> $(function () { $("#datagrid" ...

  2. python脚步管理工具supervisor=3.3.0的安装、使用。基于linux系统。

    一.安装相关包 sudo apt-get install python-pip       #python的安装包的工具 sudo apt-get install python-dev       # ...

  3. Andrew N.G的机器学习公开课学习笔记(一):机器学习的动机与应用

    机器学习由对于人工智能的研究而来,是一个综合性和应用性学科,可以用来解决计算机视觉/生物学/机器人和日常语言等各个领域的问题,机器学习的目的是让计算机具有像人类的学习能力,这样做是因为我们发现,计算机 ...

  4. ffmpeg.exe dos下怎么用 放在哪里

    系统:windows 7 1.先看dos界面,win7下这里输入cmd, 看路径 2.把下载的ffmpeg.exe复制到这个路径下  3.这就可以用命令了 1.mp4说明这个文件是跟ffmpeg.ex ...

  5. dockerRegistry搭建

    docker registry安装: 官方仓库下载registry     pull镜像: fu@ubuntu:~$ sudo docker pull registry    运行镜像 : sudo ...

  6. HtmlUnit初探

    HtmlUnit是一个用java实现的浏览器,是一个无界面的浏览器(headless browser),跟phatomJS好像是同一类事物. HtmlUnit基于apache httpClient,而 ...

  7. 解决Centos/Redhat,命令不存在

    [root@26 ~]# lsb_release                    #不存在-bash: lsb_release: command not found    [root@26 ~] ...

  8. CSS编写技巧

    1.尽量少的使用全局的重置代码 全局重置代码:*{margin:0; padding:0;}对于熟悉CSS的人来说并不陌生,并且有很多人的第一句CSS代码就是这句.它可以避免不同浏览器的默认间距不同而 ...

  9. php 字符串和数字比较一些问题

    本文章来给大家介绍关于php 字符串和数字比较一些问题,因为数字与字符在php中是不同的数据类型,所以在比较时可能会有很多的问题. ,1,2等等,其中0标示成功,其他表示不同的错误代码.程序通过 if ...

  10. iOS 适配https(AFNetworking3.0为例)

    众所周知,苹果有言,从2017年开始,将屏蔽http的资源,强推https楼主正好近日将http转为https,给还没动手的朋友分享一二 1.准备证书 首先找后台要一个证书(SSL证书,一般你跟后台说 ...