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 (中国标准时 ...
随机推荐
- 《Linux内核分析》 第一节 计算机是如何工作的
第一节 计算机是如何工作的 张嘉琪 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-100002900 ...
- git hub 使用心得
git中重要的概念: 工作目录(working directory):在工作目录中修改文件,修改后的文件状态是modified,新添加的文件是untracked,通过git add命令将文件保存到st ...
- “数学口袋精灵”第二个Sprint计划---第一天
“数学口袋精灵”第二个Sprint计划----第一天进度 任务分配: 冯美欣:欢迎界面的音效 吴舒婷:游戏界面的动作条,选择答案后的音效 林欢雯:完善算法代码的设计 进度: 冯美欣:上网百度音乐资 ...
- visual studio-2013之单元测试
安装个vs把一个寝室搞得欲仙欲死的,,已经安装好了vs2013,那些欲仙欲死的事就都不说了.开始我们的正式作业——单元测试. 要做单元测试前提有: 1.要有Unit Test Generator工具 ...
- 从编译DotNetOpenAuth中学到的程序集强签名知识
1. 背景 最近在研究DotNetOpenAuth——OAuth的一个.NET开源实现,官方网站:http://dotnetopenauth.net/ . 从GitHub签出DotNetOpenAut ...
- 使用maven的插件进行maven项目的打包
1 maven项目打包的插件有3种 maven-jar-plugin maven-assembly-plugin maven-shade-plugin 2 maven-jar-plugin 现在要新增 ...
- 微信 小程序组件 加入购物车全套 one wxml
<!--pages/shop/shop.wxml--> <view wx:if="{{hasList}}"> <view class="co ...
- java自定义注解学习(三)_注解解析及应用
上篇文章已经介绍了注解的基本构成信息.这篇文章,主要介绍注解的解析.毕竟你只声明了注解,是没有用的.需要进行解析.主要就是利用反射机制在运行时进行查看和利用这些信息 常用方法汇总 在Class.Fie ...
- Spring之jdbcTemplate:增删改
JdbcTemplate增删改数据操作步骤:1.导入jar包:2.设置数据库信息:3.设置数据源:4.调用jdbcTemplate对象中的方法实现操作 package helloworld.jdbcT ...
- Java进阶之路
Java进阶之路——从初级程序员到架构师,从小工到专家. 怎样学习才能从一名Java初级程序员成长为一名合格的架构师,或者说一名合格的架构师应该有怎样的技术知识体系,这是不仅一个刚刚踏入职场的初级程序 ...