Aho-Corasick (AC) 自动机
基础:AC自动机是建立在 trie 树和 kmp 基础之上的,为什么这么说,因为AC自动机是基于字典树的数据结构之上的,其次它是一个自动机,用到了 kmp 的失配数组的思想。
应用:在模式匹配的问题中,如果模板有很多个,可以用AC自动机来求解。
结构:字典树结构:
Fail数组(失配数组):如果现在已经匹配到一个结点,如果匹配失败,则将指正转移到 Fail 指针指向的地方,这样就不用回溯而直接匹配下去了。(举个例子:如abce和bcd,我们找到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) 自动机的更多相关文章
- AC 自动机
		AC自动机(Aho-Corasick Automata)是经典的多模式匹配算法.从前我学过这个算法,但理解的不深刻,现在已经十分不明了了.现在发觉自己对大部分算法的掌握都有问题,决定重写一系列博客把学 ... 
- 中文分词系列(二) 基于双数组Tire树的AC自动机
		秉着能偷懒就偷懒的精神,关于AC自动机本来不想看的,但是HanLp的源码中用户自定义词典的识别是用的AC自动机实现的.唉-没办法,还是看看吧 AC自动机理论 Aho Corasick自动机,简称AC自 ... 
- 多模字符串匹配算法-Aho–Corasick
		背景 在做实际工作中,最简单也最常用的一种自然语言处理方法就是关键词匹配,例如我们要对n条文本进行过滤,那本身是一个过滤词表的,通常进行过滤的代码如下 for (String document : d ... 
- HDU 2222 Keywords Search(AC自动机模版题)
		Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others ... 
- HDU 3065 病毒侵袭持续中(AC自动机)
		这题数据太水,一开始没有加上Get的方法也能AC..话说AC自动机中一定要注意加上Get的方法!(不然,同一个后缀的其他单词就没被算上了.) 代码如下: #include <stdio.h> ... 
- HDU 2222 Keywords Search(AC自动机入门)
		题意:给出若干个单词和一段文本,问有多少个单词出现在其中.如果两个单词是相同的,得算两个单词的贡献. 分析:直接就是AC自动机的模板了. 具体见代码: #include <stdio.h> ... 
- UVA - 11468  (AC自动机+动态规划)
		建立AC自动机,把AC自动机当做一张图,在上面跑L个节点就行了. 参考了刘汝佳的代码,发现可能有一个潜在的Bug--如果模式串中出现了没有指定的字符,AC自动机可能会建立出错. 提供一组关于这个BUG ... 
- hdu4787 AC自动机加分块
		这题说的是 有n次操作 +w 表示读入一个字符串,?p 询问这个字符串的子串在那些模板串中有多少个, http://blog.csdn.net/qq574857122/article/details/ ... 
- BZOJ 1444 [Jsoi2009]有趣的游戏 (AC自动机 + 概率DP + Gauss)
		1444: [Jsoi2009]有趣的游戏 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1382 Solved: 498[Submit][Statu ... 
- [意识流]简单易懂的AC自动机
		为了一言不合就徒手敲AC自动机,决定看一下原理 于是花了一张图, 参考HDU2222的样例 于是看懂这张图的你很快就敲出了如下代码并且AC了 #include<bits/stdc++.h> ... 
随机推荐
- Redis-复制(MasterSlave)
			Redis的复制(Master/Slave) 是什么: 行话:也就是我们所说的主从复制,主机数据更新后根据配置和策略, 自动同步到备机的master/slaver机制,Master以写为主,Slave ... 
- max=(a>b)?a:b;
			这个函数的意思是如果a>b,max=a:否则max=b. 实际程序: while ((__HAL_UART_GET_FLAG(huart, Flag) ? SET : RESET) == Sta ... 
- opencv python:Canny边缘提取
			Canny是边缘提取算法,在1986年提出的 是一个很好的边缘检测器 Canny算法介绍 非最大信号抑制: 高低阈值连接: example import cv2 as cv import numpy ... 
- 极简的js点击组图切换效果
			程序员进行前端开发时,时常要用到点击切换组图的动画效果,网上确实有很多此类插件,但是都很麻烦,乌糟糟无数代码,有那个看的时间,自己都能把功能写完了.在这里我提供一段极简的js点击组图切换效果代码,包含 ... 
- maven搭建父子项目
			父工程:父工程又称为父控制器,只是一个简单的工程,不能单独运行.作用是将子模块跟子工程聚合在一起.父控制器中的pom.xml配置,在子模块跟子工程中都可以被继承. 子工程:项目中创建的具有业务逻辑并且 ... 
- js中的局部函数和全局函数的调用
			//局部函数和全局函数的特点 function fc1(){ var name ="chenhao"; function fc2(){ var age = 30; alert(na ... 
- js函数声明外面使用小括号括起来再接一个小括号的写法
			js函数声明外面使用小括号括起来再接一个小括号的写法 (function(){})(); (function(){}()); !function(){}(); 总结ps:意思将函数声明变成,直接执行的 ... 
- mysql 多次分组查询 数据最大的一行
			SELECT B, D, Max(E)FROM `总表`WHERE B = '张士建'GROUP BY B, D 通过查询创建工具 编写查询语句 
- C语言:判断t所指字符串中的字母是否由连续递增字母组成。-判断一个输入的任何整数n,是否等于某个连续正整数序列之和。-将一副扑克牌编号为1到54,以某种方式洗牌,这种方式是将这副牌分成两半,然后将他们交叉,并始终保持编号1的牌在最上方。
			//判断t所指字符串中的字母是否由连续递增字母组成. #include <stdio.h> #include <string.h> void NONO(); int fun( ... 
- 【Go语言系列】第三方框架和库——GIN:GIN介绍
			1.Gin 是什么? Gin 是一个用 Go (Golang) 编写的 HTTP web 框架. 它是一个类似于 martini 但拥有更好性能的 API 框架, 由于 httprouter,速度提高 ... 
