aho-corasick php,Aho-Corasick自动机浅析

"AC自动机不是随便yy三分钟就搞定的算法么?"某犇如是说。蒟蒻MX默默流下了眼泪……

由于各种机缘巧合和本人的智力因素,我在离开OI一年多后,终于搞清楚了AC自动机(Aho-Chorasick string match algorithm)。网络上介绍AC自动机的算法多是借助KMP算法(Knuth-Morris-Pratt algorithm)中的失配数组来写,但明明AC自动机是先于KMP的,因此我决定完全扔掉和KMP相关的东西,写一篇像我这样理解力比较低下的同学也能看的懂的AC自动机算法讲解。

在讲述AC自动机之前,先简单讲讲自动机是什么。自动机是计算理论的一个概念,其实是一张“图”,每个点是一个“状态”,而边则是状态之间的转移,根据条件能指导从一个状态走向另一个状态。很多字符串匹配算法都是基于自动机模型的,比如被广泛使用的正则表达式。

AC自动机算法算是比较简单直观的字符串匹配自动机,它其实就是在一颗Trie树上建一些失配指针,当失配时只要顺着失配指针走,就能避免一些重复的计算。比如对于字符串antibody和tide,如果第一个串匹配到第5个字符(b)失配了可以直接走入第二个串的第3个字符(d)进行匹配,因为前面的“ti”是公共的,如果能匹配到第一个串的第5个字符,那么前面两个肯定是ti。

所以AC自动机分为三部分:

1.建Trie树

2.在Trie树上建立失配指针,成为AC自动机

3.自动机上匹配字符串

首先我们先建构AC自动机的数据结构。既然基础是Trie树,我们就用树的结构描述它。本文程序均用C++编写。以下是Trie树节点的结构体:

struct node

{

node *fail; //失配指针

node *child[CHAR_SET_SIZE]; //儿子节点

int point; //标识这是第几个模式串的中止节点

node(){

fail=NULL;

for (int i=0;i

point=-1; //非模式串中止节点,用-1表达

}

};

然后就是Trie树的插入,其实也是很简单、很模板的:

void Insert(char *s,int num)

{

node *p=Root;

for (char *c=s;*c!='\0';++c)

{

int t=(*c)-'a';

if (p->child[t]==NULL)

{

p->child[t]=new node;

}

p=p->child[t];

if ((*(c+1))=='\0') p->point=num;

}

}

AC自动机的精髓在于失配指针!失配指针的构建方法是这样的:对于一个节点C,标识字符a,顺着C的父亲节点的失配指针走,走到第一个有儿子也是a的节点T那么C的失配指针就指向T的标识a的儿子节点。如果找不到这个节点,那么失配指针指向Root。在实际操作时,是用广搜来实现,因为这个建构过程要求父亲节点的失配指针已经建好,而且一层层都要建好。代码如下:

void BuildFailPoint()

{

int Qh=0,Qt=1;

Q[1]=Root;

while (Qh

{

node *now=Q[++Qh];

for (int i=0;i

{

if (now->child[i]!=NULL)

{

if (now==Root) now->child[i]->fail=Root;

else

{

node *p=now->fail;

while (p!=NULL)

{

if (p->child[i]!=NULL)

{

now->child[i]->fail=p->child[i];

break;

}

p=p->fail;

}

if (p==NULL) now->child[i]->fail=Root;

}

Q[++Qt]=now->child[i];

}

}

}

}

这样,AC自动机就建构完成了!现在对查询串进行匹配。匹配过程中,需要一个p指针指向上一步成功匹配的节点。如果当前字符c失配,则p要沿着自己的失配指针走,直到新的p有一个儿子标识c,如果走不到,嘿嘿,已经回到根了。因为有可能会同时匹配多个串,所以需要扫一遍所有可以匹配的串。代码如下:

vector > Query()

{

vector > Ret; //查询返回值是第几个模式串在什么位置成功匹配

int Len=strlen(QueryString);

node *p=Root;

for (int i=0;i!=Len;++i)

{

int index=QueryString[i]-'a';

while (p->child[index]==NULL && p!=Root) p=p->fail;

if (p->child[index]==NULL) continue;

p=p->child[index];

node *t=p;

while (t!=Root) //扫所有可以匹配的串

{

if (t->point!=-1) Ret.push_back(make_pair(t->point,i));

t=t->fail;

}

}

return Ret;

}

AC自动机核心的部分就这么多,我写了一个完整的AC自动机模板程序放在最后,仅供参考,如需使用该程序,请自便,无需告知我。

#include

#include

#include

#include

#include

#include

using std::string;

using std::vector;

using std::pair;

using std::make_pair;

#define CHAR_SET_SIZE 26

#define PATTERN_SIZE 300

#define QUERY_SIZE 3000

#define QSIZE 300000

struct node

{

node *fail;

node *child[CHAR_SET_SIZE];

int point;

node(){

fail=NULL;

for (int i=0;i

point=-1;

}

};

node *Q[QSIZE];

node *Root;

vector Pattern_Collection;

void Init()

{

Root=new node;

}

void Insert(char *s,int num)

{

node *p=Root;

for (char *c=s;*c!='\0';++c)

{

int t=(*c)-'a';

if (p->child[t]==NULL)

{

p->child[t]=new node;

}

p=p->child[t];

if ((*(c+1))=='\0') p->point=num;

}

}

void InputPattern()

{

printf("Input number of patterns:");

fflush(stdout);

int N;

scanf("%d",&N);

char s[PATTERN_SIZE];

for (int i=1;i<=N;++i)

{

scanf("%s",s);

Pattern_Collection.push_back(s);

Insert(s,Pattern_Collection.size()-1);

}

}

void BuildFailPoint()

{

int Qh=0,Qt=1;

Q[1]=Root;

while (Qh

{

node *now=Q[++Qh];

for (int i=0;i

{

if (now->child[i]!=NULL)

{

if (now==Root) now->child[i]->fail=Root;

else

{

node *p=now->fail;

while (p!=NULL)

{

if (p->child[i]!=NULL)

{

now->child[i]->fail=p->child[i];

break;

}

p=p->fail;

}

if (p==NULL) now->child[i]->fail=Root;

}

Q[++Qt]=now->child[i];

}

}

}

}

char QueryString[QUERY_SIZE];

vector > Query()

{

vector > Ret;

int Len=strlen(QueryString);

node *p=Root;

for (int i=0;i!=Len;++i)

{

int index=QueryString[i]-'a';

while (p->child[index]==NULL && p!=Root) p=p->fail;

if (p->child[index]==NULL) continue;

p=p->child[index];

node *t=p;

while (t!=Root)

{

if (t->point!=-1) Ret.push_back(make_pair(t->point,i));

t=t->fail;

}

}

return Ret;

}

void InputQuery()

{

printf("Input the query string:\n");

scanf("%s",QueryString);

vector < pair > QueryAns=Query();

for (int i=0;i!=QueryAns.size();++i)

{

printf("Found pattern \"%s\" at %d\n",

Pattern_Collection[QueryAns[i].first].c_str(),

QueryAns[i].second-Pattern_Collection[QueryAns[i].first].size()+1);

}

}

int main()

{

Init();

InputPattern();

BuildFailPoint();

InputQuery();

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
08-10
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值