AC算法学习笔记
1、算法流程图
(1) void Init()
此函数是初始化函数,用来给fail数组和goto数组初始化值。

(2) void GotoFunction(string x)
这个函数的作用是生成有限自动机状态转移图。

(3) void FailFunction(int target,int k)
这是fail函数,核心内容是求出每个状态的fail值。

(4) void UpdateOutput()
这是update输出函数。其作用是更新每个状态的输出值。


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

(6)int 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算法学习笔记的更多相关文章
- AC自动机学习笔记-2(Trie图&&last优化)
我是连月更都做不到的蒟蒻博主QwQ 考虑到我太菜了,考完noip就要退役了,所以我决定还是把博客的倒数第二篇博客给写了,也算是填了一个坑吧.(最后一篇?当然是悲怆のnoip退役记啦QAQ) 所以我们今 ...
- C / C++算法学习笔记(8)-SHELL排序
原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...
- Manacher算法学习笔记 | LeetCode#5
Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...
- Johnson算法学习笔记
\(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...
- 某科学的PID算法学习笔记
最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...
- Johnson 全源最短路径算法学习笔记
Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...
- AC自动机板子题/AC自动机学习笔记!
想知道484每个萌新oier在最初知道AC自动机的时候都会理解为自动AC稽什么的,,,反正我记得我当初刚知道这个东西的时候,我以为是什么神仙东西,,,(好趴虽然确实是个对菜菜灵巧比较难理解的神仙知识点 ...
- 算法学习笔记——sort 和 qsort 提供的快速排序
这里存放的是笔者在学习算法和数据结构时相关的学习笔记,记录了笔者通过网络和书籍资料中学习到的知识点和技巧,在供自己学习和反思的同时为有需要的人提供一定的思路和帮助. 从排序开始 基本的排序算法包括冒泡 ...
- R语言实现关联规则与推荐算法(学习笔记)
R语言实现关联规则 笔者前言:以前在网上遇到很多很好的关联规则的案例,最近看到一个更好的,于是便学习一下,写个学习笔记. 1 1 0 0 2 1 1 0 0 3 1 1 0 1 4 0 0 0 0 5 ...
随机推荐
- NodeJs 开发微信公众号(四)微信网页授权
微信的网页授权指的是在微信公众号中访问第三方网页时获取用户地理.个人等信息的权限.对于开发了自己的网页app应用时,获取个人的信息非常重要.上篇博客讲到了注册时可以获取用户的信息,很多人会问为什么还需 ...
- .NET基础拾遗(4)委托、事件、反射与特性
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...
- 如何开发一个简单的HTML5 Canvas 小游戏
原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...
- TODO:Go语言goroutine和channel使用
TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理.使用的时候在函数前面加"go"这个 ...
- oracle11g 重装操作系统后,如何利用原有oracle表空间文件还原数据库
最近由于系统重装,在还原dmp备份文件时,由于数据原因(用exp命令导出时表没有导出全部),导致系统不能正常运行.根据网上的信息和个人实际情况,做个记录,便于后用. oracle 导出空表方法: 1. ...
- 基于Caffe的DeepID2实现(中)
小喵的唠叨话:我们在上一篇博客里面,介绍了Caffe的Data层的编写.有了Data层,下一步则是如何去使用生成好的训练数据.也就是这一篇的内容. 小喵的博客:http://www.miaoerduo ...
- 你真的会玩SQL吗?和平大使 内连接、外连接
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...
- c#编程基础之枚举
枚举的意义就在于限制变量取值范围. 当可以确定的几种取值时才可以用. 如果输入一个字符串需要进行判断是否是我们需要的字符串时,则一般需要这样写: using System; using System. ...
- 【十大经典数据挖掘算法】SVM
[十大经典数据挖掘算法]系列 C4.5 K-Means SVM Apriori EM PageRank AdaBoost kNN Naïve Bayes CART SVM(Support Vector ...
- 『.NET Core CLI工具文档』(六)dotnet 命令
说明:本文是个人翻译文章,由于个人水平有限,有不对的地方请大家帮忙更正. 原文:dotnet command 翻译:dotnet 命令 名称 dotnet -- 运行命令行命令的一般驱动程序 概要 d ...