Aho-Corasick 多模式匹配算法、AC自动机详解
Aho-Corasick算法是多模式匹配中的经典算法,目前在实际应用中较多。
Aho-Corasick算法对应的数据结构是Aho-Corasick自动机,简称AC自动机。
搞编程的一般都应该知道自动机FA吧,具体细分为:确定性有限状态自动机(DFA)和非确定性有限状态自动机NFA。普通的自动机不能进行多模式匹配,AC自动机增加了失败转移,转移到已经输入成功的文本的后缀,来实现。
1.多模式匹配
多模式匹配就是有多个模式串P1,P2,P3...,Pm,求出所有这些模式串在连续文本T1....n中的所有可能出现的位置。
例如:求出模式集合{"nihao","hao","hs","hsr"}在给定文本"sdmfhsgnshejfgnihaofhsrnihao"中所有可能出现的位置。
2.Aho-Corasick算法
使用Aho-Corasick算法需要三步:
1.建立模式的Trie
2.给Trie添加失败路径
3.根据AC自动机,搜索待处理的文本
下面说明这三步:
2.1建立多模式集合的Trie树
Trie树也是一种自动机。对于多模式集合{"say","she","shr","he","her"},对应的Trie树如下,其中红色标记的圈是表示为接收态:

2.2为多模式集合的Trie树添加失败路径,建立AC自动机
构造失败指针的过程概括起来就一句话:设这个节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的节点。然后把当前节点的失败指针指向那个字母也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root。
使用广度优先搜索BFS,层次遍历节点来处理,每一个节点的失败路径。
特殊处理:第二层要特殊处理,将这层中的节点的失败路径直接指向父节点(也就是根节点)。

2.3根据AC自动机,搜索待处理的文本
从root节点开始,每次根据读入的字符沿着自动机向下移动。
当读入的字符,在分支中不存在时,递归走失败路径。如果走失败路径走到了root节点,则跳过该字符,处理下一个字符。
因为AC自动机是沿着输入文本的最长后缀移动的,所以在读取完所有输入文本后,最后递归走失败路径,直到到达根节点,这样可以检测出所有的模式。
3.Aho-Corasick算法代码示例
模式串集合:{"nihao","hao","hs","hsr"}
待匹配文本:"sdmfhsgnshejfgnihaofhsrnihao"
代码:
#include<iostream>
#include<string.h>
#include<malloc.h>
#include <queue>
using namespace std; typedef struct node{
struct node *next[]; //接收的态
struct node *par; //父亲节点
struct node *fail; //失败节点
char inputchar;
int patterTag; //是否为可接收态
int patterNo; //接收态对应的可接受模式
}*Tree,TreeNode;
char pattern[][]={"nihao","hao","hs","hsr"}; /**
申请新的节点,并进行初始化
*/
TreeNode *getNewNode()
{
int i;
TreeNode* tnode=(TreeNode*)malloc(sizeof(TreeNode));
tnode->fail=NULL;
tnode->par=NULL;
tnode->patterTag=;
for(i=;i<;i++)
tnode->next[i]=NULL;
return tnode;
} /**
将Trie树中,root节点的分支节点,放入队列
*/
int nodeToQueue(Tree root,queue<Tree> &myqueue)
{
int i;
for (i = ; i < ; i++)
{
if (root->next[i]!=NULL)
myqueue.push(root->next[i]);
}
return ;
} /**
建立trie树
*/
Tree buildingTree()
{
int i,j;
Tree root=getNewNode();
Tree tmp1=NULL,tmp2=NULL;
for(i=;i<;i++)
{
tmp1=root;
for(j=;j<strlen(pattern[i]);j++) ///对每个模式进行处理
{
if(tmp1->next[pattern[i][j]-'a']==NULL) ///是否已经有分支,Trie共用节点
{
tmp2=getNewNode();
tmp2->inputchar=pattern[i][j];
tmp2->par=tmp1;
tmp1->next[pattern[i][j]-'a']=tmp2;
tmp1=tmp2;
}
else
tmp1=tmp1->next[pattern[i][j]-'a'];
}
tmp1->patterTag=;
tmp1->patterNo=i;
}
return root;
} /**
建立失败指针
*/
int buildingFailPath(Tree root)
{
int i;
char inputchar;
queue<Tree> myqueue;
root->fail=root;
for(i=;i<;i++) ///对root下面的第二层进行特殊处理
{
if (root->next[i]!=NULL)
{
nodeToQueue(root->next[i],myqueue);
root->next[i]->fail=root;
}
} Tree tmp=NULL,par=NULL;
while(!myqueue.empty())
{
tmp=myqueue.front();
myqueue.pop();
nodeToQueue(tmp,myqueue); inputchar=tmp->inputchar;
par=tmp->par; while(true)
{
if(par->fail->next[inputchar-'a']!=NULL)
{
tmp->fail=par->fail->next[inputchar-'a'];
break;
}
else
{
if(par->fail==root)
{
tmp->fail=root;
break;
}
else
par=par->fail->par;
}
}
}
return ;
} /**
进行多模式搜索,即搜寻AC自动机
*/
int searchAC(Tree root,char* str,int len)
{
TreeNode *tmp=root;
int i=;
while(i < len)
{
int pos=str[i]-'a';
if (tmp->next[pos]!=NULL)
{
tmp=tmp->next[pos];
if(tmp->patterTag==) ///如果为接收态
{
cout<<i-strlen(pattern[tmp->patterNo])+<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl;
}
i++;
}
else
{
if(tmp==root)
i++;
else
{
tmp=tmp->fail;
if(tmp->patterTag==) //如果为接收态
cout<<i-strlen(pattern[tmp->patterNo])+<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl;
}
}
}
while(tmp!=root)
{
tmp=tmp->fail;
if(tmp->patterTag==)
cout<<i-strlen(pattern[tmp->patterNo])+<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl;
}
return ;
} /**
释放内存,DFS
*/
int destory(Tree tree)
{
if(tree==NULL)
return ;
queue<Tree> myqueue;
TreeNode *tmp=NULL; myqueue.push(tree);
tree=NULL;
while(!myqueue.empty())
{
tmp=myqueue.front();
myqueue.pop(); for (int i = ; i < ; i++)
{
if(tmp->next[i]!=NULL)
myqueue.push(tmp->next[i]);
}
free(tmp);
}
return ;
} int main()
{
char a[]="sdmfhsgnshejfgnihaofhsrnihao";
Tree root=buildingTree(); ///建立Trie树
buildingFailPath(root); ///添加失败转移
cout<<"待匹配字符串:"<<a<<endl;
cout<<"模式"<<pattern[]<<" "<<pattern[]<<" "<<pattern[]<<" "<<pattern[]<<" "<<endl<<endl;
cout<<"匹配结果如下:"<<endl<<"位置\t"<<"编号\t"<<"模式"<<endl;
searchAC(root,a,strlen(a)); ///搜索
destory(root); ///释放动态申请内存
return ;
}
输出:

(上面的两个图,参考网页:http://www.cppblog.com/mythit/archive/2009/04/21/80633.html)
Aho-Corasick 多模式匹配算法、AC自动机详解的更多相关文章
- [转] AC自动机详解
转载自:http://hi.baidu.com/nialv7/item/ce1ce015d44a6ba7feded52d AC自动机详解 AC自动机是用来处理多串匹配问题的,即给你很多串,再给你一篇文 ...
- AC自动机详解
概述 AC自动机全称Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法. 考虑这样一个场景,给出L个模式字符串(加总长度为N),以及长度为M大文本, ...
- AC自动机详解 (P3808 模板)
AC自动机笔记 0.0 前言 哇,好久之前就看了 KMP 和 Trie 树,但是似乎一直没看懂 AC自动机?? 今天灵光一闪,加上之前看到一些博客和视频,瞬间秒懂啊... 其实这个玩意还是蛮好理解的. ...
- AC自动机详解(附加可持久化AC自动机)
AC自动机 AC自动机,说白了就是在trie树上跑kmp(其实个人感觉比kmp容易理解).是一种多匹配串,单个主串的匹配.概括来说,就是将多个匹配串构造一个trie树,对于每个trie树的节点构造nx ...
- BS模式的模型结构详解
编号:1004时间:2016年4月12日16:59:17功能:BS模式的模型结构详解 URL:http://blog.csdn.net/icerock2000/article/details/4000 ...
- Vmware在NAT模式下网络配置详解
Vmware在NAT模式下网络配置详解 Linux中的网络配置对于接触Linux不久的小白菜来说,还是小有难度的,可能是不熟悉这种与windows系列迥然不同的命令行操作,也可能是由于对Linux的结 ...
- 【转】AC算法详解
原文转自:http://blog.csdn.net/joylnwang/article/details/6793192 AC算法是Alfred V.Aho(<编译原理>(龙书)的作者),和 ...
- 深入剖析Redis系列: Redis集群模式搭建与原理详解
前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...
- 各个模式的accesstoken续期详解
一些预备知识 jwt的时间格式 转换为时间可以用js, new Date(1531841745*1000) ==>Tue Jul 17 2018 23:35:45 GMT+0800 (中国标准时 ...
随机推荐
- 20135337朱荟潼 Linux第六周学习总结——进程的描述和进程的创建
朱荟潼 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课http://mooc.study.163.com/course/USTC 1000029000 第六周 进程的描述 ...
- 2017-2018-2 1723《程序设计与数据结构》第三周作业 & 实验一 总结
作业地址 第三周作业:https://edu.cnblogs.com/campus/besti/CS-IMIS-1723/homework/1667 提交情况如图: 实验一:https://edu.c ...
- PAT 1001. A+B Format 解题
GitHub PDF 1001. A+B Format (20) Calculate a + b and output the sum in standard format -- that is, t ...
- 第一章:帝国的余晖 AT&T公司
启示:自己的想法,有好的技术比什么都重要,一定要注意的是技术,不要贪小便宜,明白自己最先关心的的哪个事情. 书中内容:没有人能活两百岁,也没有公司能辉煌两百年,这就是规律,很难超越.
- Mac安装Appium的Android环境
1.下载android sdk http://down.tech.sina.com.cn/page/45703.html 2.解压,配置环境变量 ANDROID_HOME=/Users/wp/ ...
- Windows 64位环境的Java 服务配置
有个任务,需要远程起调Windows64服务器下的程序,那么需要在Windows服务器中注入一个deamon服务,都知道Linux环境做成后台服务非常简单,nohup &很快能解决问题,但wi ...
- WP-PostViews使用
1.在后台安装次插件 2.获取多少天之内的访问排名最高的记录 2.1 添加相应方法代码到wp-postviews.php文件中,据体代码可以网上找(本人自己可以在自己本机的例子查看到),这里只是记录大 ...
- java 23种设计模式 深入浅出
以下内容只作为对自己对知识进行总结,如有引用他人文章会在文段末尾表明出处: Java的23种设计模式 23种设计模式总共可以分为三大类,进行不定期更新总结,将逐步展开介绍自己对设计模式的理解,多多指教 ...
- Go匿名函数
1.GO语言的匿名函数就是闭包 基本概念 闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义.要执行的代码块(由于自由变 ...
- BZOJ4520 CQOI2016K远点对(KD-Tree+堆)
堆维护第k大,每个点KD-Tree上A*式查询较远点,跑得飞快,复杂度玄学. #include<iostream> #include<cstdio> #include<c ...