字符串(3)AC自动机
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];
}
}
主串匹配
接下来是一道模板题:
#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 ;
}
这还是一道模板题:
#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自动机的更多相关文章
- 字符串处理-AC自动机
估计在OJ上刷过题的都会对AC自动机这个名词很感兴趣,同样,记得去年ACM暑期集训的时候,在最后讲到字符串部分,听说了这个算法的名字之后就对于它心向往之,AC正好是Accept的简称,字面意义上的理解 ...
- 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 ...
- HDU-2222 Keywords Search 字符串问题 AC自动机
题目链接:https://cn.vjudge.net/problem/HDU-2222 题意 给一些关键词,和一个待查询的字符串 问这个字符串里包含多少种关键词 思路 AC自动机模版题咯 注意一般情况 ...
- 字符串(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 ...
- HDU-2896 病毒侵袭 字符串问题 AC自动机
题目链接:https://cn.vjudge.net/problem/HDU-2896 题意 中文题 给一些关键词和一个字符串,问字符串里包括了那几种关键词 思路 直接套模版 改insert方法,维护 ...
- HDU-3065 病毒侵袭持续中 字符串问题 AC自动机
题目链接:https://cn.vjudge.net/problem/HDU-3065 题意 跟上一道题是几乎一模一样,这次是统计关键词的出现次数 一个相当坑的地方,注意多组样例 思路 套模版 改in ...
- 字符串(AC自动机):COCI 2015 round 5 divljak
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAy0AAANaCAIAAAALVTQoAAAgAElEQVR4nOy9X2hbx773PXfrQgQjDq
- 字符串:AC自动机
给出一个字典和一个模式串,问模式串中出现几个字典中的单词 最后一行是大串,之前输入的是小串 #include<iostream> #include<cstdio> using ...
- 多模字符串匹配算法之AC自动机—原理与实现
简介: 本文是博主自身对AC自动机的原理的一些理解和看法,主要以举例的方式讲解,同时又配以相应的图片.代码实现部分也予以明确的注释,希望给大家不一样的感受.AC自动机主要用于多模式字符串的匹配,本质上 ...
- poj 1625 (AC自动机好模版,大数好模版)
题目 给n个字母,构成长度为m的串,总共有n^m种.给p个字符串,问n^m种字符串中不包含(不是子串)这p个字符串的个数. 将p个不能包含的字符串建立AC自动机,每个结点用val值来标记以当前节点为后 ...
随机推荐
- @Valid注解的使用(转)
原文地址:http://blog.csdn.net/xzmeasy/article/details/76098188 @Valid注解用于校验,所属包为:javax.validation.Valid. ...
- python基础概念(转)
基础回顾: 1.集合 集合有2个重要作用:关系测试(并集,差集,交集)和去重. 2.文件编码 2.7上默认文件编码是ASCII码,因为不支持中文,就出了GB2312,在2.7上要支持中文就必须申明文件 ...
- Tomcat服务器下载、安装、配置环境变量教程(超详细)
请先配置安装好Java的环境,若没有安装,请参照我以下的步骤进行安装! 请先配置安装好Java的环境,若没有安装,请参照我以下的步骤进行安装! 请先配置安装好Java的环境,若没有安装,请参照我以下上 ...
- Js元素拖拽功能实现
Js元素拖拽功能实现 需要解决的问题 最近项目遇到了一个问题,就是用户某个操作需要弹出一个自定义的内容输入框,但是有个缺点,当浏览太大的时候没办法点击确认和取消按钮,应为这个弹出框是采用绝对定位的,取 ...
- SpringCloud 过滤器
在网关中配置过滤器 验证签名 package com.kps.zuul.filter; import com.kps.common.BodyReaderHttpServletRequestWrappe ...
- 4.1 socket
socket 背景概念 脑图结构 OSI 模型 socket 概念特性 脑图结构 理解示意图 额外补充 Socket是应用层与 TCP/IP协议族通信的中间软件抽象层,它是一组接口. 在设计模式中 ...
- python学习日记(内置函数)
目前所有内置函数 http://www.runoob.com/python3/python3-built-in-functions.html *菜鸟教程* 内置函数分类 作用域相关globals(). ...
- Mybatis Generator的model生成中文注释,支持oracle和mysql(通过实现CommentGenerator接口的方法来实现)
自己手动实现的前提,对maven项目有基本的了解,在本地成功搭建了maven环境,可以参考我之前的文章:maven环境搭建 项目里新建表时model,mapper以及mapper.xml基本都是用My ...
- docker_监控
目录 Docker 自带的监控子命令 sysdig Weave Scope cAdvisor Prometheus Prometheus 的架构 多维数据模型 Docker 自带的监控子命令 ps d ...
- Python【第三篇】文件操作、字符编码
一.文件操作 文件操作分为三个步骤:文件打开.操作文件.关闭文件,但是,我们可以用with来管理文件操作,这样就不需要手动来关闭文件. 实现原理: import contextlib @context ...