(并不能自动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+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;}

②trie上建ac自动机。

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];    }}}}

用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=100+3;
const int mod=10007;
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;
int n,m;
int f[N][N*N];
char s[N];
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%s",s+1);ac.ins(s);}ac.build();int tot=ac.cnt;f[0][0]=1;for(int i=0;i<=m-1;i++){for(int j=0;j<=tot;j++){if(!ac.exi[j]){for(int k=0;k<26;k++){if(!ac.exi[ac.a[j][k]]){f[i+1][ac.a[j][k]]=(f[i+1][ac.a[j][k]]+f[i][j])%mod;}}}}}long long ans=1;for(int i=1;i<=m;i++){ans=(ans*26)%mod;}for(int i=0;i<=tot;i++){if(!ac.exi[i]){ans=(ans-f[m][i]+mod)%mod;}}printf("%lld",ans);return 0;
}

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

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

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

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

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

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

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

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

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

[POI2000]病毒

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

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

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

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

转载于:https://www.cnblogs.com/Miracevin/p/9416253.html

AC自动机——多个kmp匹配相关推荐

  1. 数据结构与算法之美笔记——基础篇(下):图、字符串匹配算法(BF 算法和 RK 算法、BM 算法和 KMP 算法 、Trie 树和 AC 自动机)

    图 如何存储微博.微信等社交网络中的好友关系?图.实际上,涉及图的算法有很多,也非常复杂,比如图的搜索.最短路径.最小生成树.二分图等等.我们今天聚焦在图存储这一方面,后面会分好几节来依次讲解图相关的 ...

  2. ac自动机 匹配最长前缀_AC自动机算法

    AC自动机简介: 首先简要介绍一下AC自动机:Aho-Corasick automation,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法之一.一个常见的例子就是给出n个单词,再给出一段包 ...

  3. 字符串处理【AC自动机】 - 原理 AC自动机详解

    字符串处理[AC自动机] - 原理 AC自动机详解 AC自动机(Aho-Corasick automaton)在1975年产生于贝尔实验室,是著名的多模匹配算法. 学习AC自动机,要有KMP和Trie ...

  4. ac自动机,自动ac机(bushi

    哈哈哈哈我学明白了也许吧.挂几篇题解: b站的这个up讲得太好啦:https://www.bilibili.com/video/BV1uJ411Y7Eg?p=3&vd_source=f5170 ...

  5. bzoj2938(ac自动机)

    刚学了ac自动机,去hzwer上找了道练习题: 串是安全的就说明ac自动机不会找到匹配,考虑ac自动机的匹配过程: 我们把val等于1的点删掉和fail指针指向被删掉的点删掉: 如果剩下的图有环,就有 ...

  6. UVa 11468 (AC自动机 概率DP) Substring

    将K个模板串构成一个AC自动机,那些能匹配到的单词节点都称之为禁止节点. 然后问题就变成了在Tire树上走L步且不经过禁止节点的概率. 根据全概率公式用记忆化搜索求解. 1 #include < ...

  7. [USACO12Jan][luogu3041] Video Game Combos [AC自动机+dp]

    题面 传送门 思路 首先,有一个非常显然的思路就是dp: 设$dp[i][j]$表示前i个字符,最后一个为j 然后发现这个东西有后效性 改!设$dp[i][j]$代表前i个字符,最后15个的状态为j( ...

  8. ac自动机 匹配最长前缀_别再暴力匹配字符串了,高效的KMP,才是真的香

    如果你想了解KMP算法,请静下心读完这篇文章,一定不会辜负你的时间 暴力匹配(BF) 字符串匹配是我们在编程中常见的问题,其中从一个字符串(主串)中检测出另一个字符串(模式串)是一个非常经典的问题,当 ...

  9. 牛客 - Connie(AC自动机+dp/KMP+dp)

    题目链接:点击查看 题目大意:给出一个匹配串 sss,现在问模式串 ttt 的期望得分.其中假设匹配串在模式串中的出现次数为 xxx,那么将得到 2x2^x2x 的分数 题目分析:涉及到了期望一开始还 ...

最新文章

  1. NSJSONSerliazition文档翻译和使用
  2. 想学python有什么用-Python为什么这么火?学习python有什么用?
  3. 查看电脑python虚拟环境-python虚拟环境--virtualenv
  4. Android实现文字一个一个显示出来
  5. C++ Primer 第五版 第7章类 7.1——类讲解(成员函数、非成员函数、构造函数)
  6. Linux 两台服务器之间传输文件
  7. 反应堆模式最牛的那篇论文--由solidmango执笔翻译
  8. Android学习笔记(十一)——从意图返回结果
  9. mysql:Cannot load driver class: com.mysql.jdbc.Driver
  10. 小程序的学习资料收集
  11. 现代科技概论_现代科技概论课程:科学技术史2
  12. php全站文章搜索,phpspider在列表页生成内容采集url.(又名:如何通过搜狗搜索关键词,爬取新浪新闻)...
  13. python拆分PDF
  14. 论文快报-2021-10-Multi-task optimization and evolutionary multitasking
  15. 电商相关:SKU概念
  16. 网页禁止粘贴的解决方法(以学习通网页为例)
  17. Oracle更新(update)
  18. Synchronized锁的使用
  19. access按职称计算平均年龄_计算机二级ACCESS考试操作题
  20. 汇文opac的openlink.php改造

热门文章

  1. 深入学习heritrix---体系结构(Overview of the crawler)
  2. python把list的所有元素生成排列和组合
  3. C#.Net 如何动态加载与卸载程序集(.dll或者.exe)0-------通过应用程序域AppDomain加载和卸载程序集...
  4. 创建服务器及请求响应等简单的node.js例子
  5. 智能指针变量做函数参数的一个值得注意的地方
  6. 大学计算机基础数据库知识点,大学计算机基础试题(数据库)
  7. python 数据比对 函数_用python比对csv文件中的数据
  8. python工具用什么语言_Python中一些自然语言工具的使用的入门教程
  9. AndroidTestCase常用的两段配置
  10. Android文件命名规范