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"
代码:
1 #include<iostream>2 #include<string.h>3 #include<malloc.h>4 #include <queue>5 using namespace std;6 7 typedef struct node{8 struct node *next[26]; //接收的态9 struct node *par; //父亲节点10 struct node *fail; //失败节点11 char inputchar;12 int patterTag; //是否为可接收态13 int patterNo; //接收态对应的可接受模式14 }*Tree,TreeNode;15 char pattern[4][30]={"nihao","hao","hs","hsr"};16 17 /**18 申请新的节点,并进行初始化19 */20 TreeNode *getNewNode()21 {22 int i;23 TreeNode* tnode=(TreeNode*)malloc(sizeof(TreeNode));24 tnode->fail=NULL;25 tnode->par=NULL;26 tnode->patterTag=0;27 for(i=0;i<26;i++)28 tnode->next[i]=NULL;29 return tnode;30 }31 32 /**33 将Trie树中,root节点的分支节点,放入队列34 */35 int nodeToQueue(Tree root,queue<Tree> &myqueue)36 {37 int i;38 for (i = 0; i < 26; i++)39 {40 if (root->next[i]!=NULL)41 myqueue.push(root->next[i]);42 }43 return 0;44 }45 46 /**47 建立trie树48 */49 Tree buildingTree()50 {51 int i,j;52 Tree root=getNewNode();53 Tree tmp1=NULL,tmp2=NULL;54 for(i=0;i<4;i++)55 {56 tmp1=root;57 for(j=0;j<strlen(pattern[i]);j++) ///对每个模式进行处理58 {59 if(tmp1->next[pattern[i][j]-'a']==NULL) ///是否已经有分支,Trie共用节点60 {61 tmp2=getNewNode();62 tmp2->inputchar=pattern[i][j];63 tmp2->par=tmp1;64 tmp1->next[pattern[i][j]-'a']=tmp2;65 tmp1=tmp2;66 }67 else68 tmp1=tmp1->next[pattern[i][j]-'a'];69 }70 tmp1->patterTag=1;71 tmp1->patterNo=i;72 }73 return root;74 }75 76 /**77 建立失败指针78 */79 int buildingFailPath(Tree root)80 {81 int i;82 char inputchar;83 queue<Tree> myqueue;84 root->fail=root;85 for(i=0;i<26;i++) ///对root下面的第二层进行特殊处理86 {87 if (root->next[i]!=NULL)88 {89 nodeToQueue(root->next[i],myqueue);90 root->next[i]->fail=root;91 }92 }93 94 Tree tmp=NULL,par=NULL;95 while(!myqueue.empty())96 {97 tmp=myqueue.front();98 myqueue.pop();99 nodeToQueue(tmp,myqueue); 100 101 inputchar=tmp->inputchar; 102 par=tmp->par; 103 104 while(true) 105 { 106 if(par->fail->next[inputchar-'a']!=NULL) 107 { 108 tmp->fail=par->fail->next[inputchar-'a']; 109 break; 110 } 111 else 112 { 113 if(par->fail==root) 114 { 115 tmp->fail=root; 116 break; 117 } 118 else 119 par=par->fail->par; 120 } 121 } 122 } 123 return 0; 124 } 125 126 /** 127 进行多模式搜索,即搜寻AC自动机 128 */ 129 int searchAC(Tree root,char* str,int len) 130 { 131 TreeNode *tmp=root; 132 int i=0; 133 while(i < len) 134 { 135 int pos=str[i]-'a'; 136 if (tmp->next[pos]!=NULL) 137 { 138 tmp=tmp->next[pos]; 139 if(tmp->patterTag==1) ///如果为接收态 140 { 141 cout<<i-strlen(pattern[tmp->patterNo])+1<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl; 142 } 143 i++; 144 } 145 else 146 { 147 if(tmp==root) 148 i++; 149 else 150 { 151 tmp=tmp->fail; 152 if(tmp->patterTag==1) //如果为接收态 153 cout<<i-strlen(pattern[tmp->patterNo])+1<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl; 154 } 155 } 156 } 157 while(tmp!=root) 158 { 159 tmp=tmp->fail; 160 if(tmp->patterTag==1) 161 cout<<i-strlen(pattern[tmp->patterNo])+1<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl; 162 } 163 return 0; 164 } 165 166 /** 167 释放内存,DFS 168 */ 169 int destory(Tree tree) 170 { 171 if(tree==NULL) 172 return 0; 173 queue<Tree> myqueue; 174 TreeNode *tmp=NULL; 175 176 myqueue.push(tree); 177 tree=NULL; 178 while(!myqueue.empty()) 179 { 180 tmp=myqueue.front(); 181 myqueue.pop(); 182 183 for (int i = 0; i < 26; i++) 184 { 185 if(tmp->next[i]!=NULL) 186 myqueue.push(tmp->next[i]); 187 } 188 free(tmp); 189 } 190 return 0; 191 } 192 193 int main() 194 { 195 char a[]="sdmfhsgnshejfgnihaofhsrnihao"; 196 Tree root=buildingTree(); ///建立Trie树 197 buildingFailPath(root); ///添加失败转移 198 cout<<"待匹配字符串:"<<a<<endl; 199 cout<<"模式"<<pattern[0]<<" "<<pattern[1]<<" "<<pattern[2]<<" "<<pattern[3]<<" "<<endl<<endl; 200 cout<<"匹配结果如下:"<<endl<<"位置\t"<<"编号\t"<<"模式"<<endl; 201 searchAC(root,a,strlen(a)); ///搜索 202 destory(root); ///释放动态申请内存 203 return 0; 204 }
输出:
(上面的两个图,参考网页:http://www.cppblog.com/mythit/archive/2009/04/21/80633.html)
Aho-Corasick 多模式匹配算法、AC自动机详解相关推荐
- 字符串处理【AC自动机】 - 原理 AC自动机详解
字符串处理[AC自动机] - 原理 AC自动机详解 AC自动机(Aho-Corasick automaton)在1975年产生于贝尔实验室,是著名的多模匹配算法. 学习AC自动机,要有KMP和Trie ...
- Aho-Corasick 多模式匹配算法(AC自动机) 的算法详解及具体实现
多模式匹配 多模式匹配就是有多个模式串P1,P2,P3-,Pm,求出所有这些模式串在连续文本T1-.n中的所有可能出现的位置. 例如:求出模式集合{"nihao","ha ...
- [数据结构]模式匹配算法--KMP算法详解
目录 一. 模式匹配 二. 模式匹配算法 1. 朴素模式匹配算法 2. KMP算法 1). KMP算法的优势 2). KMP算法的原理 3). next数组的构造 4). 利用next数组匹配的过程 ...
- android doze模式源码分析,Android Doze模式启用和恢复详解
从Android 6.0(API level 23)开始,Android提出了两个延长电池使用时间的省电特性给用户.用户管理可以在没有充电的情况下管理app的行为.当用户一段时间没有使用手机的时候,D ...
- python的编程模式-Python设计模式之状态模式原理与用法详解
本文实例讲述了Python设计模式之状态模式原理与用法.分享给大家供大家参考,具体如下: 状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 ...
- 深入剖析Redis系列(三) - Redis集群模式搭建与原理详解
前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...
- 怎样进入android模式,安卓手机如何进入Recovery模式的通用方式详解
2014-12-12 15:24:16 安卓手机如何进入Recovery模式的通用方式详解 标签:安卓 Recovery模式 教程 Recovery模式是什么?这里说的Recovery模式主要指的是安 ...
- Java单机部署,Nacos docker单机模式部署实现过程详解
Nacos 的部署,我使用的时docker 部署(单机模式 mysql),官网文档:https://nacos.io/zh-cn/docs/quick-start-docker.html 拉取代码: ...
- 设计模式 - 抽象工厂模式(abstract factory pattern) 详解
抽象工厂模式(abstract factory pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/270916 ...
最新文章
- 漫画:什么是计数排序?
- 如何将商业第三方文物整合到您的Maven版本中
- 【MySQL】基于MySQL的SQL增删改查实战演练
- CSM管理系统_复选框删除多条记录
- 人工智能和分布式账本如何帮助我们解决假新闻
- iOS UIPageControl
- 损失函数的“噪音免疫力”
- centos 卸载 jdk
- 2022年第十三届蓝桥杯省赛--难度评价
- imx6ul pinctrl 驱动浅析
- python 打包exe_python打包exe能运行但是没有结果解决方案
- 摄影测量内定向编程实现
- 146条经典偏方(祖传秘方)
- C#基础 连接数据库
- PgAdmin出现Utility file not found. Please correct the Binary Path in the Preferences dialog的解决办法
- Smart Thief 问题
- Mob平台获取手机验证码
- 微信小程序跳转美团外卖小程序时出现白屏解决demo
- 一个假猪套神器:NET CAT-NC
- Android高版本联网失败报错:Cleartext HTTP traffic to xxx not permitted解决方法
热门文章
- 大学生计算机基础大难,大学生计算机基础实训六样文
- Ubuntu 18.04 U盘启动安装教程【图文教程,非常详细!!!!】
- PO1382(贪心)
- js将当前时间格式化为年-月-日 时:分:秒
- web前端作业--响应式美食菜谱网页设计(HTML+CSS+JavaScript+)实现
- Chapter 5. Monte Carlo Methods
- 用户日活月活怎么统计 - Redis HyperLogLog 详解
- 如何通过二极管设计一个与门电路
- Latex修改局部字体大小
- 以终为始,向死而生——5月份英语总结