基础:AC自动机是建立在 trie 树和 kmp 基础之上的,为什么这么说,因为AC自动机是基于字典树的数据结构之上的,其次它是一个自动机,用到了 kmp 的失配数组的思想。

应用:在模式匹配的问题中,如果模板有很多个,可以用AC自动机来求解。

结构:字典树结构:

Fail数组(失配数组):如果现在已经匹配到一个结点,如果匹配失败,则将指正转移到 Fail 指针指向的地方,这样就不用回溯而直接匹配下去了。(举个例子:如abcebcd,我们找到c发现下一个要找的不是e,就跳到bcd中的c处,看看此处的下一个字符(d)是不是应该找的那一个)。由此可见, Fail 数组可用一个 BFS 求得。

上上图的 Fail 数组指向图:

ashe为例:其匹配过程如下:

说了这么多,下面直接上模板:

建树:

const int maxn =  2e6+10;
int tree[maxn][26]; //字典树
int point[maxn]; //记录该单词出现次数
int Fail[maxn]; //失败时的回溯指针
int tot = 0; //结点个数
void insert(char *s) //同字典树;建树
{
int root = 0;
int len=strlen(s);
for(int i=0;i<len;i++){
int id = s[i] - 'a';
if(!tree[root][id])
tree[root][id] = ++tot;
root = tree[root][id];
}
point[root]++; //当前节点单词数+1
}

求 Fail 数组( BFS ):

void getFail()						//求Fail(失配)数组
{
Fail[0]=0;
queue <int>q;
for(int i=0;i<26;i++) //将第二层所有出现了的字母扔进队列
{
if(tree[0][i]){
Fail[tree[0][i]] = 0; //第一层结点肯定全都指向根节点
q.push(tree[0][i]);
}
} // fail[now] -> 当前节点now的失败指针指向的地方
// tire[now][i] -> 下一个字母为i+'a'的节点的下标为tire[now][i] while(!q.empty())
{
int now = q.front();
q.pop();
for(int i=0;i<26;i++) //查询26个字母
{
if(tree[now][i]){ //如果有这个子节点为字母i+'a',则 //让这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个节点)
//有点绕,为了方便理解特意加了括号 Fail[tree[now][i]] = tree[Fail[now]][i];
q.push(tree[now][i]);
}
else //否则就让当前节点的这个子节点指向当前节点Fail指针的这个子节点
tree[now][i] = tree[Fail[now]][i];
}
}
}

查询:

int query(char *s)
{
int root = 0,ans = 0;
ine len=strlen(s);
for(int i=0;i<len;i++) //遍历文本串
{
int id=s[i]-'a';
root = tree[root][id]; //从s[i]点开始寻找
for(int j=now;j && point[j]!=-1;j=Fail[j]){
//一直向下寻找,直到匹配失败(失败指针指向根或者当前节点已找过).
ans += point[j];
point[j] = -1; //将遍历国后的节点标记,防止重复计算
}
}
return ans;
}

模板AC代码:

#include<bits/stdc++.h>

using namespace std;
const int maxn=2e6+10;
typedef long long ll;
int tree[maxn][27];
int point[maxn],tot=0,Fail[maxn];
char s[10004][55];
char str[1000005];
void insert(char *s)
{
int len=strlen(s);
int root=0;
for(int i=0;i<len;++i)
{
int id=s[i]-'a';
if(!tree[root][id])
tree[root][id] = ++tot;
root=tree[root][id];
}
point[root]++;
}
void getFail()
{
Fail[0]=0;
queue<int> q;
for(int i=0;i<26;++i)
{
if(tree[0][i]){
Fail[tree[0][i]]=0;
q.push(tree[0][i]);
}
}
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<26;++i)
{
if(tree[now][i]){
Fail[tree[now][i]]=tree[Fail[now]][i];
q.push(tree[now][i]);
}
else
tree[now][i]=tree[Fail[now]][i];
}
}
}
ll query(char *s)
{
int root=0,res=0;
int len=strlen(s);
for(int i=0;i<len;++i)
{
int id=s[i]-'a';
root=tree[root][id];
for(int j=root; j && point[j]!=-1;j=Fail[j]){
res+=point[j];
point[j]=-1;
}
}
return res;
}
void init()
{
for(int i=0;i<=tot;++i)
{
point[i]=0;
Fail[i]=0;
for(int j=0;j<26;++j){
tree[i][j]=0;
}
}
tot=0;
} int main()
{
//ios::sync_with_stdio(false); int T;
scanf("%d",&T);
memset(point,0,sizeof(point));
while(T--)
{
int n;
cin>>n;
for(int i=0;i<n;++i){
scanf("%s",&s[i]);
insert(s[i]);
}
getFail();
scanf("%s",&str);
int res=query(str);
printf("%d\n",res);
init();
} system("pause");
return 0;
}

Aho-Corasick (AC) 自动机的更多相关文章

  1. AC 自动机

    AC自动机(Aho-Corasick Automata)是经典的多模式匹配算法.从前我学过这个算法,但理解的不深刻,现在已经十分不明了了.现在发觉自己对大部分算法的掌握都有问题,决定重写一系列博客把学 ...

  2. 中文分词系列(二) 基于双数组Tire树的AC自动机

    秉着能偷懒就偷懒的精神,关于AC自动机本来不想看的,但是HanLp的源码中用户自定义词典的识别是用的AC自动机实现的.唉-没办法,还是看看吧 AC自动机理论 Aho Corasick自动机,简称AC自 ...

  3. 多模字符串匹配算法-Aho–Corasick

    背景 在做实际工作中,最简单也最常用的一种自然语言处理方法就是关键词匹配,例如我们要对n条文本进行过滤,那本身是一个过滤词表的,通常进行过滤的代码如下 for (String document : d ...

  4. HDU 2222 Keywords Search(AC自动机模版题)

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

  5. HDU 3065 病毒侵袭持续中(AC自动机)

    这题数据太水,一开始没有加上Get的方法也能AC..话说AC自动机中一定要注意加上Get的方法!(不然,同一个后缀的其他单词就没被算上了.) 代码如下: #include <stdio.h> ...

  6. HDU 2222 Keywords Search(AC自动机入门)

    题意:给出若干个单词和一段文本,问有多少个单词出现在其中.如果两个单词是相同的,得算两个单词的贡献. 分析:直接就是AC自动机的模板了. 具体见代码: #include <stdio.h> ...

  7. UVA - 11468 (AC自动机+动态规划)

    建立AC自动机,把AC自动机当做一张图,在上面跑L个节点就行了. 参考了刘汝佳的代码,发现可能有一个潜在的Bug--如果模式串中出现了没有指定的字符,AC自动机可能会建立出错. 提供一组关于这个BUG ...

  8. hdu4787 AC自动机加分块

    这题说的是 有n次操作 +w 表示读入一个字符串,?p 询问这个字符串的子串在那些模板串中有多少个, http://blog.csdn.net/qq574857122/article/details/ ...

  9. BZOJ 1444 [Jsoi2009]有趣的游戏 (AC自动机 + 概率DP + Gauss)

    1444: [Jsoi2009]有趣的游戏 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1382  Solved: 498[Submit][Statu ...

  10. [意识流]简单易懂的AC自动机

    为了一言不合就徒手敲AC自动机,决定看一下原理 于是花了一张图, 参考HDU2222的样例 于是看懂这张图的你很快就敲出了如下代码并且AC了 #include<bits/stdc++.h> ...

随机推荐

  1. IE6下的png不透明问题

    前几天刚做完一个小需求,但是在兼容ie方面用了比较久的时间,主要是切面那边用的背景图都是png格式的,而经过查找知道,ie6对png图片透明部分渲染效果是不透明的,我看到的是淡淡的绿色,简单的处理方式 ...

  2. 1、TensorFlow如何工作?

    TensorFlow特殊的张量计算引擎使得TensorFlow能够很好的满足机器学习的计算需要,从2015年开始发起 本书基于TensorFlow0.12+和python3.0+ 环境安装要求 pip ...

  3. Spring Boot 开发环境IDEA下的热部署

    这个知识点忘记写了,我不是很热衷于IDEA的热部署,觉得太消耗机器性能. 1 引入 Pom <!--热部署--> <dependency> <groupId>org ...

  4. 树莓派安装中文输入法Fcitx及Google拼音输入法

    本来是想给树莓派安装搜狗输入法的, 搜狗输入法Linux版:https://pinyin.sogou.com/linux/?r=pinyin 但是一直安装不成功,后面发现原来是系统架构不同导致的,搜狗 ...

  5. POJ - 1845 Sumdiv(分治)

    题意:求$A^{B}$的所有约数之和$mod\ 9901$ 思路:由结论有,一个数$n$进行质因数分解得到$n={p_{1}}^{c_{1}} * {p_{2}}^{c_{2}} *...* {p_{ ...

  6. 设计模式开始--UML类之间关系表示

    平常写代码写的比较多,没有从架构的层次了解类与类之间的关系,下面就从代码的层面论述UML中类与类质之间的关系 实线的关系要强于虚线 1.extends 表示继承 2.implements表示实现 3. ...

  7. 4 中文乱码 selenium的使用

    # 中文乱码 #处理中文乱码 import requests from lxml import etree from urllib import request url = 'http://pic.n ...

  8. 在HTML中实现两个div并排显示

    在HTML中让两个div并排显示,通常情况下有三种实现方式,包括: (1)设置为行内样式,display:inline-block (2)设置float浮动 (3)设置position定位属性为abs ...

  9. DVWA实验之Brute Force(暴力破解)- Low

    DVWA实验之Brute Force-暴力破解- Low     这里开始DVWA的相关实验~   有关DVWA环境搭建的教程请参考: https://www.cnblogs.com/0yst3r-2 ...

  10. 实现简单ORM案例

    ORM框架: • 我们希望设计一个可以实现对象和SQL自动映射的框架,但是整体用法和设计比Hibernate简单.砍掉不必要的功能.• 会穿插使用设计模式• 增加 – 将对象对应成sql语句,执行sq ...