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. jQuery中slice()用法总结

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="utf-8&quo ...

  2. 开源项目导入eclipse的一般步骤

    开源项目导入eclipse的一般步骤 周银辉 下载到开源项目后,我们还是希望导入到eclipse中还看,这样要方便点,一般的步骤是这样的 打开源代码目录, 如果看到里面有.calsspath .pro ...

  3. openstack上创建vm实例后,状态为ERROR问题解决

    问题说明:在openstack上创建虚拟机,之前已顺利创建了n个centos6.8镜像的vm现在用ubuntu14.04镜像创建vm,发现vm创建后的状态为ERROR! 1)终端命令行操作vm创建 [ ...

  4. [[其他教程]] 2015年最新版iOS基础视频_最适合初学者入门

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

  5. 在基于vue的webpack脚手架开发中使用了代理转发,结果浏览器发出的请求中不带cookie导致登录时总是session失效怎么办?

    环境:            有2个业务接口需要转发到82的服务器上:     ../user/getCode.do     ../user/doLogin.do 现象:          使用上述的 ...

  6. JavaScript 特殊对象 Array-Like Objects 详解

    这篇文章拖了有两周,今天来跟大家聊聊 JavaScript 中一类特殊的对象 -> Array-Like Objects. (本文节选自 underscore 源码解读系列文章,完整版请关注 h ...

  7. [转]java selenium webdriver实战 应用小结

    原文链接:http://www.cnblogs.com/itliucheng/p/5578788.html 部分api 1.访问网站 driver.get("http://www.baidu ...

  8. [转]EL表达式和JSTL表达式实例

    /* **Title:EL表达式和JSTL表达式实例 **Author:Insun **Blog:http://yxmhero1989.blog.163.com/ */ 为了方便写JSP,我们引入了E ...

  9. spoj 371 Boxes

    N个盒子围成一圈,第i个盒子初始时有Ai个小球,每次可以把一个小球从一个盒子移到相邻的两个盒子之一里.问最少移动多少次使得每个盒子中小球的个数不超过1. ΣAi<=N.1<=N<=1 ...

  10. opencv 比较直方图方式 进行人脸检测对比

    完整opencv(emgucv)人脸.检测.采集.识别.匹配.对比 //成对几何直方图匹配               public static string MatchHist()         ...