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 (中国标准时 ...
随机推荐
- Mabatis中#{}和${}的区别
动态 sql 是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析.mybatis 为我们提供了两种支持动态 sql ...
- 1084. Broken Keyboard (20)-水题
#include <iostream> #include <cstdio> #include <string.h> #include <algorithm&g ...
- PAT甲题题解-1121. Damn Single (25)-水题
博主欢迎转载,但请给出本文链接,我尊重你,你尊重我,谢谢~http://www.cnblogs.com/chenxiwenruo/p/6789787.html特别不喜欢那些随便转载别人的原创文章又不给 ...
- 《linux内核》课本第五章读书笔记
- C#回调函数的简单讲解与应用例子(最简单讲解,大神绕道)
本博客一直以来的宗旨就是:用最简单的方式讲清楚不复杂的问题. 因为本人也很菜所以也没法讲太复杂HHHHHH...... 所以如果哪天某个大神看到了觉得讲的有问题欢迎指出. 话不多说进入正题.. ——— ...
- 分析code
1 using System; //跟系统说明一下可能会用到这个dll里面的东西 using System.Collections.Generic; //引用集合类命名空间 using System. ...
- download & excel & blob
download & excel & blob Blob https://developer.mozilla.org/en-US/docs/Web/API/Blob FileReade ...
- Delphi中Form的position属性与代码自定义窗体位置
通过Form的Position属性可设置窗体的初始位置,如选择DesktopCenter为桌面中心,ScreenCenter为屏幕中心,等等. 这个属性在很多时候简化了程序代码. 但是,如果设置了po ...
- 异构数据库之间完全可以用SQL语句导数据
告诉你一个最快的方法,用SQLServer连接DBF 在SQLServer中执行 SELECT * into bmk FROM OpenDataSource( ‘Microsoft.Jet.OLEDB ...
- 【IneliJ 】使用IneliJ IDEA 2016将Java Web项目导出为War包
本文记录使用IDEA导出war包的过程以及碰到问题的解决办法 虽说现在改用IDEA进行开发了,但还是用eclipse打war包 ….囧 这样下去不是办法... 于是今天就试着使用IDEA进行打包. 项 ...