AC自动机真神奇,其实说白了就是在trie树上进行kmp模式匹配,不过刚接触确实有些难度,有些思想确实有些难以理解,所以学习的时候最好亲自手动模拟整个算法的全过程,那我就来写篇blog总结一下。

首先我们需要明白AC自动机是用来干什么的,首先我们知道kmp算法是用来解决单模式串匹配问题的,那么如果模式串不止一个,我们该怎么办呢?没错,AC自动机。我们可以把所有的模式串建立一棵字典树,然后在字典树上进行自我匹配建立next数组,最后利用next数组与主串进行匹配。

建立trie树没有什么问题,最难的地方估计是建立next数组的过程,那我就来手动模拟一下。

假设模式串为:AAAA  ABA  BBA BBB

主串为:AAAABBABABABB

首先我们建立字典树:

不过AC自动机里的字典树和普通的trie有所不同,这里的trie定义了一个0号虚结点,并且0号结点的所有出边都连向一号点,也就是说我们可以理解为1号结点代表的所有的字符集,然后我们将一号点的next指向0号点。

对于2号结点,我们有f[2]=1(其中f[i]表示i结点的父结点)那么我们就看一下1号点的next指向的0号点是否含有A这个儿子。显然1号结点就是这样的结点,所以2号点的next连向1。

同样的我们对于三号点也进行同样的操作,由于一号点的next是0,而0有B这样的儿子,所以把3的next连向1。

对于其余的结点我们也进行一样的操作:

但是对于8号点,它的父亲是5号点,5号点的next为3号点,然而三号点没有A这个儿子结点,那我们就继续查询3号点的next 1号点,一号点有A这个儿子,所以把8号点的next指向2号点。

然后我们就可以建立整棵trie树的next数组了。

这里有一个问题,我们在询问8号点时,重复跳了几次next这样便使得时间复杂度超过我们期望的O(n),所以我们需要进行一些神奇的操作。

我们在询问三号结点A这个儿子的时候,由于它不存在,一般情况我们就会continue,然后继续询问他的其它儿子,但是我们在询问9号点时,再一次访问了不存在的3号点的A这个儿子,而我们又会继续访问3号点的next所指的结点的A这个儿子,也就是说3号点的A这个儿子在整个操作中完全没有作用但是我们还会重复访问,所以我们就直接把3号点的A儿子直接定义为2号点,也就是next[3]:1的A儿子。这样我们在询问8号点的时候就可以直接将next[9]赋值为9的父结点的next结点的A这个儿子,也就是2号点。这样我们就是实现了O(n)的复杂度来建立next数组。(刚刚接触可能不是很理解,自己多画图模拟就明白了)

建立起next数组后,我们就可以直接让主串在trie树上跑,然后就可以愉快的dp了。

 void trie(char *s)
{
int len=strlen(s),u=;
for(int i=;i<len;i++)
{
int c=s[i]-'a';
if(!tree[u][c])
{
tree[u][c]=++tot;
}
u=tree[u][c];
}
bo[u]++;//记录每个模式串结尾的位置
}

建立trie树

 void bfs()
{
for(int i=;i<=;i++)
tree[][i]=;//把0的所有出边都设为1
next[]=;q.push();//把1的next记为0,1号点入队
while(q.size())
{
int u=q.front();
q.pop();
for(int i=;i<=;i++)
{
if(!tree[u][i])
tree[u][i]=tree[next[u]][i];//这里就是上文所述的优化 如果u没有i这个儿子,
//那就把next[u]的i这个儿子当做u的i这个儿子
else
{
q.push(tree[u][i]);
int v=next[u];
next[tree[u][i]]=tree[v][i];
}
}
}
}

求next数组

 void find(char *s)
{
int u=,len=strlen(s),k;
for(int i=;i<len;i++)
{
int c=s[i]-'a';
u=tree[u][c];
}
}

主串匹配

接下来是一道模板题:

P3808 【模板】AC自动机(简单版)

 #include<iostream>
#include<string>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<map>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#define maxn 1000005
using namespace std; inline int read()
{
int x=,res=;
char c=getchar();
while(c<''||c>'')
{
if(c=='-')
x=-;
c=getchar();
}
while(c>=''&&c<='')
{
res=res*+(c-'');
c=getchar();
}
return res*x;
} int n,tot=,ans;
char a[maxn];
int tree[maxn][],next[maxn],bo[maxn];
queue<int>q; void trie(char *s)
{
int len=strlen(s),u=;
for(int i=;i<len;i++)
{
int c=s[i]-'a';
if(!tree[u][c])
{
tree[u][c]=++tot;
}
u=tree[u][c];
}
bo[u]++;
} void bfs()
{
for(int i=;i<=;i++)
tree[][i]=;
next[]=;q.push();
while(q.size())
{
int u=q.front();
q.pop();
for(int i=;i<=;i++)
{
if(!tree[u][i])
tree[u][i]=tree[next[u]][i];
else
{
q.push(tree[u][i]);
int v=next[u];
next[tree[u][i]]=tree[v][i];
}
}
}
} void find(char *s)
{
int u=,len=strlen(s),k;
for(int i=;i<len;i++)
{
int c=s[i]-'a';
k=tree[u][c];
while(k>&&bo[k]!=-)
{
ans+=bo[k];
bo[k]=-;
k=next[k];
}
u=tree[u][c];
}
} int main()
{
n=read();
for(int i=;i<=n;i++)
{
scanf("%s",a);
trie(a);
}
bfs();
scanf("%s",a);
find(a);
cout<<ans;
return ;
}

这还是一道模板题:

P3796 【模板】AC自动机(加强版)

 #include<iostream>
#include<string>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<map>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#define maxn 1000005
using namespace std; struct tr
{
int next;
int vis[];
int end;
int num;
}tree[]; inline int read()
{
int x=,res=;
char c=getchar();
while(c<''||c>'')
{
if(c=='-')
x=-;
c=getchar();
}
while(c>=''&&c<='')
{
res=res*+(c-'');
c=getchar();
}
return res*x;
} int n,tot,ans;
char b[][];
char a[maxn];
int f[];
queue<int>q; void trie(char *s,int num)
{
int len=strlen(s),u=;
for(int i=;i<len;i++)
{
int c=s[i]-'a';
if(!tree[u].vis[c])
{
tree[u].vis[c]=++tot;
}
u=tree[u].vis[c];
}
tree[u].num=num;
} void bfs()
{
for(int i=;i<=;i++)
tree[].vis[i]=;
tree[].next=;q.push();
while(q.size())
{
int u=q.front();
q.pop();
for(int i=;i<=;i++)
{
if(!tree[u].vis[i])
tree[u].vis[i]=tree[tree[u].next].vis[i];
else
{
q.push(tree[u].vis[i]);
int v=tree[u].next;
tree[tree[u].vis[i]].next=tree[v].vis[i];
}
}
}
} void find(char *s)
{
int len=strlen(s),u=,k;
for(int i=;i<len;i++)
{
int c=s[i]-'a';
k=tree[u].vis[c];
while(k>)
{
f[tree[k].num]++;
k=tree[k].next;
}
u=tree[u].vis[c];
}
} int main()
{
while()
{
n=read();
if(n==) break;
memset(tree,,sizeof(tree));
memset(f,,sizeof(f));
tot=;ans=;
for(int i=;i<=n;i++)
{
scanf("%s",b[i]);
trie(b[i],i);
}
bfs();
scanf("%s",a);
find(a);
for(int i=;i<=n;i++)
{
ans=max(ans,f[i]);
}
cout<<ans<<endl;
for(int i=;i<=n;i++)
{
if(f[i]==ans)
printf("%s\n",b[i]);
}
}
return ;
}

字符串(3)AC自动机的更多相关文章

  1. 字符串处理-AC自动机

    估计在OJ上刷过题的都会对AC自动机这个名词很感兴趣,同样,记得去年ACM暑期集训的时候,在最后讲到字符串部分,听说了这个算法的名字之后就对于它心向往之,AC正好是Accept的简称,字面意义上的理解 ...

  2. 2017ACM暑期多校联合训练 - Team 8 1006 HDU 6138 Fleet of the Eternal Throne (字符串处理 AC自动机)

    题目链接 Problem Description The Eternal Fleet was built many centuries ago before the time of Valkorion ...

  3. HDU-2222 Keywords Search 字符串问题 AC自动机

    题目链接:https://cn.vjudge.net/problem/HDU-2222 题意 给一些关键词,和一个待查询的字符串 问这个字符串里包含多少种关键词 思路 AC自动机模版题咯 注意一般情况 ...

  4. 字符串(AC自动机):HDU 5129 Yong Zheng's Death

    Yong Zheng's Death Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 512000/512000 K (Java/O ...

  5. HDU-2896 病毒侵袭 字符串问题 AC自动机

    题目链接:https://cn.vjudge.net/problem/HDU-2896 题意 中文题 给一些关键词和一个字符串,问字符串里包括了那几种关键词 思路 直接套模版 改insert方法,维护 ...

  6. HDU-3065 病毒侵袭持续中 字符串问题 AC自动机

    题目链接:https://cn.vjudge.net/problem/HDU-3065 题意 跟上一道题是几乎一模一样,这次是统计关键词的出现次数 一个相当坑的地方,注意多组样例 思路 套模版 改in ...

  7. 字符串(AC自动机):COCI 2015 round 5 divljak

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAy0AAANaCAIAAAALVTQoAAAgAElEQVR4nOy9X2hbx773PXfrQgQjDq

  8. 字符串:AC自动机

    给出一个字典和一个模式串,问模式串中出现几个字典中的单词 最后一行是大串,之前输入的是小串 #include<iostream> #include<cstdio> using ...

  9. 多模字符串匹配算法之AC自动机—原理与实现

    简介: 本文是博主自身对AC自动机的原理的一些理解和看法,主要以举例的方式讲解,同时又配以相应的图片.代码实现部分也予以明确的注释,希望给大家不一样的感受.AC自动机主要用于多模式字符串的匹配,本质上 ...

  10. poj 1625 (AC自动机好模版,大数好模版)

    题目 给n个字母,构成长度为m的串,总共有n^m种.给p个字符串,问n^m种字符串中不包含(不是子串)这p个字符串的个数. 将p个不能包含的字符串建立AC自动机,每个结点用val值来标记以当前节点为后 ...

随机推荐

  1. PowerShell-自定义函数(五)-参数互斥:ParameterSetName

    转自:https://blog.51cto.com/38088444/1920978 这一篇我们来讲一下参数的互斥,何谓参数互斥呢.用九胖风格的话说就是互怼,有我没你,有你没我. 例如我们为一个Pin ...

  2. 最简单的 springboot 发送邮件,使用thymeleaf模板

    1,导入需要的包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId&g ...

  3. Python学习之路——函数对象作用域名称空间

    一.函数对象 # 函数名就是存放了函数的内存地址,存放了内存地址的变量都是对象,即 函数名 就是 函数对象 # 函数对象的应用 # 1 可以直接被引用 fn = cp_fn # 2 可以当作函数参数传 ...

  4. Python【第一篇】python安装、pip基本用法、变量、输入输出、流程控制、循环

    一.python安装 Ubuntu下 系统版本已经同时安装了python2和python3 如果没有python3,可以参考这个貌似是印度阿三的安装视频:http://v.youku.com/v_sh ...

  5. Activation HDU - 4089(概率dp)

    After 4 years' waiting, the game "Chinese Paladin 5" finally comes out. Tomato is a crazy ...

  6. Yii2的Gridview应用技巧补充

    Yii2框架下的Gridview通常用来展示一张DB表中的数据,十分方便.这里只说一下经常要用到的一些小技巧,其实大多数官方文档都是有的,只是有可能需要在多个文档里. 自动创建的gridview示例. ...

  7. 【洛谷P3899】谈笑风生

    题目大意:给定一棵 N 个节点的有根树,1 号节点为根节点,现给出 Q 个询问,每次询问距离 u 号节点不超过 K 的节点 b,c 为 a 与 b 的后代,求这样的三元组有多少个. 题解:学会了线段树 ...

  8. Wannafly挑战赛23 T2游戏 SG函数

    哎,被卡科技了,想了三个小时,最后还是大佬给我说是\(SG\)函数. \(SG\)函数,用起来很简单,证明呢?(不可能的,这辈子都是不可能的) \(SG\)定理 游戏的\(SG\)函数就是各个子游戏的 ...

  9. 20175221 实验一《Java开发环境的熟悉》实验报告

    20175221 实验一<Java开发环境的熟悉>实验报告 (一)Linux运行结果 (二)IDEA下Java程序开发.调试:学会通过调试(Debug)来定位逻辑错误   试验IDEA是否 ...

  10. Vue(小案例_vue+axios仿手机app)_首页(底部导航栏+轮播图+九宫格)

    ---恢复内容开始--- 一.前言                        1.底部导航(两种做法)                                         2.轮播图 ...