(并不能自动AC)

介绍:

Aho-Corasick automaton,最经典的处理多个模式串的匹配问题。

是kmp和字典树的结合。

精髓与灵魂:

①利用trie处理多个模式串

②引入fail指针。节点x的fail表示,trie中最大的某个前缀等于x到根节点字符串后缀的节点位置。

fail类比于kmp的nxt数组,可以在失配的时候,O(1)找到最大的可能能继续匹配的位置。

所以,ac自动机可看做多个kmp

步骤:(完整代码在下面)

①建trie树。插入模式串。

void ins(char *s){
int len=strlen(s+);
int now=;
for(int i=;i<=len;i++){
int x=s[i]-'A';
if(!a[now][x]) a[now][x]=++cnt;
now=a[now][x];
}
exi[now]=;
}

②trie上建ac自动机。

void build(){
queue<int>q;
for(int i=;i<;i++){
if(a[][i]) fail[a[][i]]=,q.push(a[][i]);
}
while(!q.empty()){
int x=q.front();q.pop();
exi[x]|=exi[fail[x]];
for(int i=;i<;i++){
if(a[x][i]){
fail[a[x][i]]=a[fail[x]][i];
q.push(a[x][i]);
}
else{
a[x][i]=a[fail[x]][i];
}
}
}
}

用bfs来建造,并且,即时转移fail指针。

fail指针的转移正确性:

因为bfs是分层加入元素,而fail至少让字符串长度-1,所以之前的fail[x]的各种信息都处理完毕了。

并且,由于fail的定义,所以能在fail[x][i]往下转移,一定就是最优的。

这里有个小优化:

else{a[x][i]=a[fail[x]][i];}

如果x没有i这个儿子,那么就直接指向它fail指针位置的儿子。

这样子,在之后的if(a[x][i])中,可以直接走一次fail[x]的儿子就可以找到真正的fail[a[x][i]]了。

因为,如果fail没有这个儿子,不加这个优化还要继续跳fail,复杂度没有保证了。

这样,就类似于并查集的路径压缩思想,直接指到最长的有这个儿子的点了。

(语言表达不好,自行画图理解吧。。。)

完整代码:

struct node{
int a[N*N][26],cnt;
int fail[N*N];
bool exi[N*N];
void init(){
memset(a,0,sizeof a);memset(exi,0,sizeof exi);
cnt=0;memset(fail,0,sizeof fail);
}
void ins(char *s){
int len=strlen(s+1);
int now=0;
for(int i=1;i<=len;i++){
int x=s[i]-'A';
if(!a[now][x]) a[now][x]=++cnt;
now=a[now][x];
}
exi[now]=1;
}
void build(){
queue<int>q;
for(int i=0;i<26;i++){
if(a[0][i]) fail[a[0][i]]=0,q.push(a[0][i]);
}
while(!q.empty()){
int x=q.front();q.pop();
exi[x]|=exi[fail[x]];
for(int i=0;i<26;i++){
if(a[x][i]){
fail[a[x][i]]=a[fail[x]][i];
q.push(a[x][i]);
}
else{
a[x][i]=a[fail[x]][i];
}
}
}
}
}ac;

另外,我们ac自动机上节点上,也可以加上其他的标记。

例题:

[JSOI2007]文本生成器

Description:

给n个模式串,求有多少个长度为m的文章,至少包含一个模式串

Solution:

Ac自动机的标志很明显,多个模式串,一个主串。

Ac自动机dp的状态很套路,一般就是匹配到j位置,怎么怎么样。。

设f[i][j],前i个字符,匹配到AC自动机的j位置,没出现一个模式串的方案数。(最后总方案-没出现一个方案,差分)

每个点有一个exi布尔数组,表示匹配到这个点,这个点所代表的前缀(可以是一个完整模式串)是否已经包含了至少一个模式串。

插入的时候,末尾exi=1,bfs的时候,exi[x]|=exi[fail[x]]即可。正确性同bfs分层图性质。

然后判断,直接转移就好了。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=+;
const int mod=;
struct node{
int a[N*N][],cnt;
int fail[N*N];
bool exi[N*N];
void init(){
memset(a,,sizeof a);memset(exi,,sizeof exi);
cnt=;memset(fail,,sizeof fail);
}
void ins(char *s){
int len=strlen(s+);
int now=;
for(int i=;i<=len;i++){
int x=s[i]-'A';
if(!a[now][x]) a[now][x]=++cnt;
now=a[now][x];
}
exi[now]=;
}
void build(){
queue<int>q;
for(int i=;i<;i++){
if(a[][i]) fail[a[][i]]=,q.push(a[][i]);
}
while(!q.empty()){
int x=q.front();q.pop();
exi[x]|=exi[fail[x]];
for(int i=;i<;i++){
if(a[x][i]){
fail[a[x][i]]=a[fail[x]][i];
q.push(a[x][i]);
}
else{
a[x][i]=a[fail[x]][i];
}
}
}
}
}ac;
int n,m;
int f[N][N*N];
char s[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%s",s+);
ac.ins(s);
}
ac.build();
int tot=ac.cnt;
f[][]=;
for(int i=;i<=m-;i++){
for(int j=;j<=tot;j++){
if(!ac.exi[j]){
for(int k=;k<;k++){
if(!ac.exi[ac.a[j][k]]){
f[i+][ac.a[j][k]]=(f[i+][ac.a[j][k]]+f[i][j])%mod;
}
}
}
}
}
long long ans=;
for(int i=;i<=m;i++){
ans=(ans*)%mod;
}
for(int i=;i<=tot;i++){
if(!ac.exi[i]){
ans=(ans-f[m][i]+mod)%mod;
}
}
printf("%lld",ans);
return ;
}

为什么要记录AC自动机上匹配到的状态呢?

因为,单纯记录这一位是哪个字符肯定不能判断是否包含。

而AC自动机本身的fail,就蕴含了所有的可能包含的位置。只要不断跳fail即可。

相当于该状态已经包罗万象。

因为fail的定义,也不会包含更多,不会包含更少。

所以对于匹配问题再适合不过了。

AC自动机dp出题人出烦了之后,

就开始出一些涉及AC自动机形态的题目,更贴近算法本身。

基本开刀处都是fail指针(AC自动机的精髓嘛)

[POI2000]病毒

这是一个利用fail指针构可能可以和trie树构成环的特性。从而构造出无限长的串。

[NOI2011]阿狸的打字机——AC自动机之fail树的利用

这个题目恰好相反,把fail树和trie树都利用起来,并且离线处理。

这两个题目都是值得思考总结的。都利用了fail的性质,但是都没有直接使用fail指针。妙哉!

AC自动机——多个kmp匹配的更多相关文章

  1. ac自动机暴力跳fail匹配——hdu5880

    很简单的题,ac自动机里再维护一个len表示每个状态的串长,用s去query时每到一个结点都要暴力跳fail,因为有可能这个结点不是,但是其fail是危险结点,找到一个就直接break 再用个差分数组 ...

  2. Codeforces 590E - Birthday(AC 自动机+Dilworth 定理+二分图匹配)

    题面传送门 AC 自动机有时只是辅助建图的工具,真的 首先看到多串问题,果断建出 AC 自动机.设 \(m=\sum|s_i|\). 不难发现子串的包含关系构成了一个偏序集,于是我们考虑转化为图论,若 ...

  3. ac自动机(tree+kmp模板)

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

  4. hdu1686 Oulipo KMP/AC自动机

    The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter 'e ...

  5. KMP与AC自动机

    KMP算法主要思想就是预处理出失配函数, 从而减少匹配失败时的回溯, 复杂度是$\Theta(m+n)$, 已达到理论下界 c++代码如下 int n, f[N]; char t[N], p[N]; ...

  6. [Alg] 文本匹配-多模匹配-AC自动机

    1. 简介 AC自动机是一种多模匹配的文本匹配算法. 如果采用naive的方法,即依次比较文本串s中是否包含模式串p1, p2,...非常耗时.考虑到这些模式串中可能具有相同子串,可以利用已经比较过的 ...

  7. 使用AC自动机解决文章匹配多个候选词问题

    解决的问题 KMP算法用于单个字符串匹配,AC自动机用于文章中匹配多个候选词. 流程 第一步,先将候选词先建立前缀树. 第二步,以宽度优先遍历的方式把前缀树的每个节点设置fail指针, 头节点的fai ...

  8. 【hdu3247-Resource Archiver】位压DP+AC自动机+SPFA

    题意:给定n个文本串,m个病毒串,文本串重叠部分可以合并,但合并后不能含有病毒串,问所有文本串合并后最短多长. (2 <= n <= 10, 1 <= m <= 1000) 题 ...

  9. 【暑假】[实用数据结构] AC自动机

    Aho-Corasick自动机  算法: <功能> AC自动机用于解决文本一个而模板有多个的问题. AC自动机可以成功将多模板匹配,匹配意味着算法可以找到每一个模板在文本中出现的位置. & ...

随机推荐

  1. [c#]记一次实验室局域网的ARP欺骗

    起因 某天中午午睡时,笔者被激烈的键盘和鼠标声音吵醒,发现实验室的同学在那边忘我地打LOL,顿觉不爽,于是决定整他一下.想了一下之后觉得就让他掉线一下作为惩罚好了.结合以往的理论知识,大家在同一个局域 ...

  2. HW 2017 12 17可禾大佬神题

    好不容易搞来的题目,不写一写怎么行呢. 不过难度真心不高(一小时K掉),都是老题+暴力题,没有欧洲玄学. 再说一句,这试卷是叶可禾出的吧. T1 好老的题目,看到有多组数据我还怕了,以为有更流弊的算法 ...

  3. Linux 平台和 Windows平台下 Unicode与UTF-8互转

    Windows: unsigned char * make_utf8_string(const wchar_t *unicode) { , index = , out_index = ; unsign ...

  4. Caffe+Windows 环境搭建收集

    Caffe+Anconda3+VS2015+Win10(64位)环境搭建 Caffe on Windows (Visual Studio 2015+CUDA8.0+cuDNNv5) Win10+VS2 ...

  5. 校内模拟赛 Label

    题意: n个点m条边的无向图,有些点有权值,有些没有.边权都为正.给剩下的点标上数字,使得$\sum\limits_{(u,v)\in E}len(u,v) \times (w[u] - w[v]) ...

  6. Elasticsearch Java Rest Client API 整理总结 (三)——Building Queries

    目录 上篇回顾 Building Queries 匹配所有的查询 全文查询 Full Text Queries 什么是全文查询? Match 全文查询 API 列表 基于词项的查询 Term Term ...

  7. 我的SQL SERVER数据库会装满吗?

    概述 今天有个客户问我一个蛮有意思的问题.我使用的SQL SERVER 2008数据库,目前数据库130多G,其中某个表的记录条数就有3亿1千多万,占用了50多G.那SQL SERVER 数据库中的表 ...

  8. 11、Dockerfile实战-Tomcat

    一.编写Dockerfile 具体步骤这里不再细说,直接看Dockerfile文件: FROM centos:7 MAINTAINER QUNXUE ENV VERSION=8.0.46 RUN yu ...

  9. okhttp3.4.1+retrofit2.1.0实现离线缓存

    关于Retrofit+OkHttp的强大这里就不多说了,还没了解的同学可以自行去百度.这篇文章主要讲如何利用Retrofit+OkHttp来实现一个较为简单的缓存策略:即有网环境下我们请求数据时,如果 ...

  10. JNI探秘-----FileInputStream的read方法详解

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章我们已经分析过File ...