1、算法流程图

(1)    void Init()

此函数是初始化函数,用来给fail数组和goto数组初始化值。

(2)    void GotoFunction(string x)

这个函数的作用是生成有限自动机状态转移图。

(3) void FailFunction(int target,int k)

这是fail函数,核心内容是求出每个状态的fail值。

(4) void UpdateOutput()

这是update输出函数。其作用是更新每个状态的输出值。

5void Check(string x)

这个是check函数,其作用是判断改状态下output函数是否有输出,如果有输出就输出相应状态下的字符串。并且决定该状态接受输入之后的去向,如果fail,则调用该状态的fail 函数来决定去向。

6int main()

主函数,整个过程的入口。



2、自动机所定义的数据结构及其功能

(1)             int Goto[M][26];

goto数组是状态机状态的载体,内部存储着本次实验的全部状态。起始状态为0,之后每获得一个有效输入就生成一个新的状态。但是在生成状态之前要进行检验,看是否已经存在本次状态。

(2)             int Fail[M];

fail数组存储的是该状态获得输入后,如果结果为fail之后的转向状态。

(3)             string Output[M];

output数组是一个字符串数组,存储的是以该状态为终结状态的字符串。当然,字符串不唯一,AC算法的核心任务之一就是找到每个状态为终结状态时候的全部输出字符串。

(4)             string Depth[M];

depth数组用来标示该状态在第几层。我们在此次实验中将goto函数创建的状态看作一个树,因此必然需要一个数组来指明树中的节点所在的层数。



3、转向函数、失效函数、输出函数的构建过程

(1)             转向函数

我们首先来看其伪代码:

结合伪代码和刚才的函数流程图,我们可以看出转向函数首先对数组进行初始化。其次,来看while循环。如果g(state,aj)!=fail,那么就将g(state,aj)赋值给state,其目的是如果已经存在的状态就不必再次创建,只需要不断地向前更新状态即可。可是如果g(state,aj)=fail,那么我们就要创建新的状态,即newstate+1,并将g(state,aj)指向此状态,再更新状态。在函数最后,构建部分output函数。

(2)             失效函数

我们来看fail函数的伪代码:

Fail函数采用队列作为核心数据结构。首先将0状态后的有效状态加入队列。如果队列不空,就会一直执行while循环中的代码。首先将队首取出,将队首能够到达的有效状态依次加入队列。求出已取出的队首的fail值并作为state。接下来判断g(state,a)是否为fail。如果不是fail,那么该值就会作为新入队列的队首的fail值。依次类推,用队列以层序的方式将状态图中每一个状态的fail值都求出来。求出了改状态的fail值之后,应该将此状态的输出并上fail状态的输出。这是很关键的一步,用以更新output数组输出值。

(3)             输出函数

同样我们来看看output函数的伪代码

Output本质就是在模拟自动机执行的过程。首先进入while循环,如果g(state,a)为fail,那么就调用改状态的fail函数,并将函数值更新给state。直到跳出while循环,之后状态往前走一步,并判断改状态是否有输出。如果有输出,就先将改状态的输出打印出来,再继续读入下一个输入。



4、  源代码

#include<iostream>

#include<string.h>

#define M 20//State_Number

using namespace std;

int Goto[M][26];

int Top;

int Fail[M];

string Output[M];

string Depth[M];

void Init()

{

Top=0;

for(int i=0;i<M;i++)

{

Fail[i]=0;

for(int j=0;j<26;j++)

{

Goto[i][j]=0;

}

Depth[i]=Output[i]="";

}

Depth[0]+='0';

}

void GotoFunction(string x)

{

int len=x.length();

int next=0;

for(int i=0;i<len;i++)

{

int index=x[i]-97;/*a->0*/

if(Goto[next][index]==0)

{

Goto[next][index]=++Top;

next=Top;

}

else

{

next=Goto[next][index];

}

char num=next+48;/*0->'0'*/

if(Depth[i+1].find(num)==Depth[i+1].npos)

{

/*

这段代码很巧妙,他本质上是用一个数组来模拟树

其作用是让i+1层囊括这一层的所有状态

*/

Depth[i+1]+=num;//每一层都有哪些状态

}

}

Output[next]+=x;//构建output数组,在next位置输出x字符串

}

void FailFunction(int target,int k)

{

for(int i=0;i<Depth[k].length();i++)

{

int num=Depth[k][i]-48;

for(int j=0;j<26;j++)

{

if(Goto[num][j]==target)

{

/*

这一段是核心代码

首先找到state

然后根据算法构建target的fail值

*/

int state=Fail[num];

Fail[target]=Goto[state][j];

return;

}

}

}

}

void UpdateOutput()

{

int k=2,num;

Fail[0]=0;

for(int i=0;i<Depth[1].length();i++)

{

num=Depth[1][i]-48;

Fail[num]=0;//当然啦,我们规定层数为一的状态fail函数值都为0

}

while(Depth[k]!="")

{

for(int i=0;i<Depth[k].length();i++)

{

num=Depth[k][i]-48;

FailFunction(num,k-1);

/*

这一段是核心代码

就好像广度优先遍历

对于每一层的每一个状态

构建其fail函数值

*/

if(Output[Fail[num]]!="")

{

Output[num]+=" ";

Output[num]+=Output[Fail[num]];

/*

当然这也是核心代码

重构output内部值

*/

}

}

k++;

}

for(int i=0;i<=Top;i++)

{

cout<<'\n'<<i<<'\t'<<Output[i];

}

}

void Check(string x)

{

int state=0,index,i=0;

while(i<x.length())

{

index=x[i]-97;

if(Goto[state][index]!=0||state==0)

{

/*

0状态无论输入什么都不报错

*/

state=Goto[state][index];

if(Output[state]!="")

{

cout<<i+1<<'\t'<<Output[state]<<'\n';

}

i++;

}

else

{

state=Fail[state];

}

}

}

int main()

{

Init();

int i=1;

cout<<"welcome the AC world!"<<endl;

cout<<"please input the "<<i <<" patterns: ";

string x;

cin>>x;

while(x!="exit")

{

i++;

cout<<"please input the "<<i <<" patterns: ";

GotoFunction(x);

cin>>x;

}

UpdateOutput();

cout<<"\n\n";

cin>>x;

Check(x);

}

AC算法学习笔记的更多相关文章

  1. AC自动机学习笔记-2(Trie图&&last优化)

    我是连月更都做不到的蒟蒻博主QwQ 考虑到我太菜了,考完noip就要退役了,所以我决定还是把博客的倒数第二篇博客给写了,也算是填了一个坑吧.(最后一篇?当然是悲怆のnoip退役记啦QAQ) 所以我们今 ...

  2. C / C++算法学习笔记(8)-SHELL排序

    原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...

  3. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

  4. Johnson算法学习笔记

    \(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...

  5. 某科学的PID算法学习笔记

    最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...

  6. Johnson 全源最短路径算法学习笔记

    Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...

  7. AC自动机板子题/AC自动机学习笔记!

    想知道484每个萌新oier在最初知道AC自动机的时候都会理解为自动AC稽什么的,,,反正我记得我当初刚知道这个东西的时候,我以为是什么神仙东西,,,(好趴虽然确实是个对菜菜灵巧比较难理解的神仙知识点 ...

  8. 算法学习笔记——sort 和 qsort 提供的快速排序

    这里存放的是笔者在学习算法和数据结构时相关的学习笔记,记录了笔者通过网络和书籍资料中学习到的知识点和技巧,在供自己学习和反思的同时为有需要的人提供一定的思路和帮助. 从排序开始 基本的排序算法包括冒泡 ...

  9. R语言实现关联规则与推荐算法(学习笔记)

    R语言实现关联规则 笔者前言:以前在网上遇到很多很好的关联规则的案例,最近看到一个更好的,于是便学习一下,写个学习笔记. 1 1 0 0 2 1 1 0 0 3 1 1 0 1 4 0 0 0 0 5 ...

随机推荐

  1. python 三元运算

    C:\Users\Administrator>pythonPython 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.190 ...

  2. MSSQLSERVER添加c# clr程序集的使用方法

    前言 MSSQLSERVER提供程序集,无疑可以让编程人员更加便捷的操作数据库数据,比如c#写的函数,可以在数据库当作sql的函数使用,你想想他对不熟悉数据库的程序员来说是有多么的嗨.这么好的机制,大 ...

  3. Maven在Eclipse中的实用小技巧

    前言     我们在开发的工程中很多都是Maven项目,这样更加便于我们jar包的管理.而我们一般使用的IDE都是Eclipse,由于我们在日常的开发过程中会经常要用到一些Maven的操作,所以我今天 ...

  4. ITTC数据挖掘系统(六)批量任务,数据查看器和自由文档

    这一次带来了一系列新特新,同时我们将会从商业智能的角度讨论软件的需求 一. 批量任务向导 一个常用的需求是完成处理多个任务,可能是同一个需求以不同的参数完成多次,这类似批量分析某一问题:或者是不同的需 ...

  5. 用H5中的Canvas等技术制作海报

    在去年的时候也实现过合成海报的功能,不过当时时间仓促,实现的比较简单. 就一个旋转功能,图片也不能拖动放大,也不能裁剪. 去年的实现可以参考<移动图片操作--上传>和<移动图片操作- ...

  6. springboot(八):RabbitMQ详解

    RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用. 消息中间件在互联网公司的使用中越来越多,刚才还看到新闻阿里将RocketMQ捐献给了apa ...

  7. 学习SpringMVC——说说视图解析器

    各位前排的,后排的,都不要走,咱趁热打铁,就这一股劲我们今天来说说spring mvc的视图解析器(不要抢,都有位子~~~) 相信大家在昨天那篇如何获取请求参数篇中都已经领略到了spring mvc注 ...

  8. C#面试题汇总(未完成)

    泛型的好处: 1.可以保证类型安全以及避免装箱和拆箱操作,泛型类会在编译时由具体的类型去取代. 2.我们就拿一个ArrayList来说吧,ArrayList要进行拆箱操作,也就是ArrayList传入 ...

  9. 通过Http接口及SolrNet 两种方法基于Solr5.5.1 实现CURD

    前言 老规矩,任何技术的入门我通常都会总结增删改查,本文我就通过HttpWebRequest和SolrNet的方式实现Solr最基础的增删改查(CURD).对于自己的完整项目,同时不想过于依赖第三方类 ...

  10. WeText项目:一个基于.NET实现的DDD、CQRS与微服务架构的演示案例

    最近出于工作需要,了解了一下微服务架构(Microservice Architecture,MSA).我经过两周业余时间的努力,凭着自己对微服务架构的理解,从无到有,基于.NET打造了一个演示微服务架 ...