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. [[其他教程]] 2015年最新版iOS基础视频_最适合初学者入门

    主讲:孙庆虎类型:iOS 适合对象:初学者入门视频介绍:本视频是iOS学院精心录制的免费精华版iOS语言基础视频,该视频特点在于最大程度保证了知识点的完整性,按知识点进行视频录制,每个视频控制在20分 ...

  2. 直接启动tomcat时为tomcat指定JDK

    第一种: 在windows环境下以批处理文件方式启动tomcat,只要运行<CATALINA_HOME>/bin/startup.bat这个文件,就可以启动Tomcat.在启动时,star ...

  3. JS组件系列——基于Bootstrap Ace模板的菜单和Tab页效果分享(你值得拥有)

    前言:最近园子里多了许多谈语言.谈环境.谈逼格(格局)的文章,看看笑笑过后,殊不知其实都是然并卵.提升自己的技术才是王道.之前博主分享过多篇bootstrap组件的文章,引起了很多园友的关注和支持,看 ...

  4. HTTP协议(二):header标头说明

    Header 解释 示例 Accept-Ranges 表明服务器是否支持指定范围请求及哪种类型的分段请求 Accept-Ranges: bytes Age 从原始服务器到代理缓存形成的估算时间(以秒计 ...

  5. Maven_profile_使用profile配置不同环境的properties(实践)

    配置方法分为以下几个步骤: 1.配置profiles节点(pom.xml) 2.配置build节点(pom.xml)--如果不配置该节点则无法找到profile中的properties属性值,并且配置 ...

  6. 反序列化问题的研究之java篇

    博客园很早就开通了,当时下决心要把自己的经验心得记录上去,但是却没有做到,因为一直觉得自己搞得东西可能还是比较的初级,感觉拿不出手,所以也就是只是把它记录在在印象笔记上面(三年下来,还是整理和收藏了一 ...

  7. Centos7下安装python,查看python版本

    安装Centos的时候,一般会自带默认安装python2.x 一般用python -V可以查看python版本. 我当时安装的时候,运行了那个语句,但是却显示了一大堆出来,虽然里面也带有版本信息,但是 ...

  8. Thinking in java学习笔记之LinkedList 与Stack

  9. gulp错误GulpUglifyError: unable to minify JavaScript解决

    这个错误是由于在打包js代码时,js语法错误导致的,修改以下js的语法即可.

  10. <<< ajax在jsp中对于https跨域不能访问

    XMLHttpRequest cannot load https://www.emaple.com. No 'Access-Control-Allow-Origin' header is presen ...