大家都不喜欢指针,但是这个AC自动机仿佛不用不行……

先引用我最喜欢的话:“AC自动机,不是自动AC的机器。” 如果写不好还可能一直WA

AC自动机是KMP与Trie树的完美结合,适用于多字符串匹配,这与KMP的单串不同

首先是一棵Trie树

记得建空节点做根,有大神忘记了建,直想砸键盘。

然后标准建树

如果用指针的话申请空间冷静一点

下一步就是构建fail指针,虽然我喜欢把它叫next,与KMP一致

幸好找到了好的版子,不然就很难搞

因为fail指针的构建顺序必须由上到下

即:fail是从下面的节点指向上面的节点的,这样不会漏配

So用BFS会很棒。

左面就是一个手画的树


void build(){
root->next=NULL;//根节点无fail
push(root);//根节点入队
while(!empty()){//BFS开始
TREE *i=front();//提取队首元素
for(int j=;j<;j++){//对每一个字母进行处理,尝试搜索
if(i->s[j]!=NULL){//搜到
if(i==root)i->s[j]->next=root;//是和根节点直接连的回根
else
{
TREE* p=i->next;//和搜索的点的fail指针的下节点找
while(p!=NULL)//继续找,所有fail都符合要求
{
if(p->s[j]!=NULL)//fail指针处有和j字母一样的点
{
i->s[j]->next=p->s[j];//更新结束
break;
}
p=p->next;//fail的fail也符合要求
}
if(p==NULL) i->s[j]->next=root;//无匹配,回根
}
q[++e]=i->s[j];//结果入队
}
}
}
}

经过一番建立,我们将所有的fail构建完成~~

长这样:

最好自己再画一个图,这样会更好的理解

然后就要搜索了,搜索和建立很像

补下:搜索的解释,上次忘写了,自己很吃亏,尴尬

void ffind(){//搜索函数
int j=;//从0号位开始搜
TREE *p=root;//另一方面树上从根节点走起
while(st[j])
{
int i=st[j]-'a';//不解释
while(p->s[i]==NULL && p!=root) p=p->next;//没找着,就顺着fail往下走
p=p->s[i];//这时只可能有两种可能,一是真没有,二是找着一个
if(p==NULL)p=root;//没有我们直接回根,相当于重置树上指针
TREE *k=p;//这个是找着后用的
while(k!=root && k->endt!=-)//去找
{
ans+=k->endt;//有结尾标记,这个是根据KeywordSearch的题意
k->endt=-;//为了防止重复计算 ,这个也是题意
k=k->next;//到所有的fail指针处找
}
j++;
}
}

如果是搜是否匹配或出现次数,在结尾写cnt计数即可

但如果要算匹配长度,我们就要打标记,再dfs一遍,像玄武密码一样

下面补上题和码

[模板]Keyword Search

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define L 1010101
using namespace std;
struct TREE{
TREE *next,*s[];
char dat;
int endt;
TREE(){
next=NULL;
memset(s,,sizeof s);
dat=endt=;
}
};
TREE *q[];
int f=,e=;
TREE *root;
int n,ans=;
char ch[L],st[L];
void add(char *now){
TREE *i=root;
int j=;
while(now[j]){
int k=now[j]-'a';
if(i->s[k]==NULL)
i->s[k]=new TREE();
i=i->s[k];
j++;//cout<<j<<endl;getchar();puts("P");
}
i->endt++;
}
bool empty(){
if(e==f){
return true;
}
return false;
}
void push(TREE *i){
e++;
q[e]=i;
}
TREE* front(){
if(empty())return NULL;
f++;
return q[f];
}
void build(){
root->next=NULL;
push(root);
while(!empty()){
TREE *i=front();
for(int j=;j<;j++){
if(i->s[j]!=NULL){
if(i==root) i->s[j]->next=root;
else
{
TREE* p=i->next;
while(p!=NULL)
{
if(p->s[j]!=NULL)
{
i->s[j]->next=p->s[j];
break;
}
p=p->next;
}
if(p==NULL) i->s[j]->next=root;
}
q[++e]=i->s[j];
}
}
}
}
void ffind(){
int j=;
TREE *p=root;
while(st[j])
{
int i=st[j]-'a';
while(p->s[i]==NULL && p!=root) p=p->next;
p=p->s[i];
if(p==NULL)p=root;
TREE *k=p;
while(k!=root && k->endt!=-)
{
ans+=k->endt;
k->endt=-;
k=k->next;
}
j++;
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
root=new TREE();
ans=;
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%s",ch);
add(ch);
}
scanf("%s",st);
build();
ffind();
printf("%d\n",ans);
}
return ;
}

[稍有思考][还是模板]玄武密码

 #include <iostream>
#include <cstdio>
#include <cstring>
#define L 111111111
#define N 111111
using namespace std;
struct ACauto{
ACauto *next,*s[];
int endp,DeepinC;
bool mark;
ACauto(){
next=NULL;
memset(s,,sizeof s);
mark=DeepinC=endp=;
}
}*root;
struct Myqueue{
ACauto *Q[];int f,e;
bool empty(){
if(e==f)return true;
return false;
}
void push(ACauto *k){
e++;
Q[e]=k;
}
ACauto* front(){
if(empty())return NULL;
f++;
return Q[f];
}
}q;
int n,ans=,cnt=;
char st[L],ch[N][];
inline int turn(char ch){
switch(ch){
case 'E':return ;
case 'W':return ;
case 'S':return ;
default :return ;
}
}
void add(char *c){
ACauto *i=root;
int j=;
while(c[j]){
int k=turn(c[j]);
if(i->s[k]==NULL)
i->s[k]=new ACauto();
i=i->s[k];
i->DeepinC=j+;
// cout<<c[j]<<" "<<i->DeepinC<<endl;
j++;
}
i->endp++;
}
void build(){
root->next=NULL;
q.push(root);
while(!q.empty()){
ACauto *i=q.front();
for(int j=;j<;j++){
if(i->s[j]!=NULL){
if(i==root) i->s[j]->next=root;
else{
ACauto *p=i->next;
while(p!=NULL){
if(p->s[j]!=NULL){
i->s[j]->next=p->s[j];
break;
}
p=p->next;
}
if(p==NULL) i->s[j]->next=root;
}
q.push(i->s[j]);
}
}
}
}
void ffind(char *ser){
int j=;
ACauto *p=root;
while(ser[j]){
int i=turn(ser[j]);
while(p->s[i]==NULL&&p!=root){
p=p->next;
p->mark=;//cout<<p<<" INP "<<j<<" "<<ser[j]<<" Depth: "<<p->mark<<" "<<p->DeepinC<<endl;
}
p=p->s[i];
if(p==NULL)p=root;
ACauto *k=p;
while(k!=root && k->endp!=-){
k->mark=;
k->endp=-;
k=k->next;//cout<<k<<" INK "<<j<<" "<<ser[j]<<" Depth: "<<k->mark<<" "<<k->DeepinC<<endl;
}
// cout<<k<<" OUTK "<<j<<" "<<ser[j]<<" Depth: "<<k->mark<<" "<<k->DeepinC<<endl;
j++;
}
}
void ser(char *c){
ACauto *i=root;
int j=;
int lsans=;
bool la=;
while(c[j]){
int k=turn(c[j]);
i=i->s[k];
if(i->mark)ans=max(i->DeepinC,ans);
j++;
}
}
int main(){
int len;
root=new ACauto();
scanf("%d%d",&len,&n);
scanf("%s",st);
for(int i=;i<=n;i++) {
scanf("%s",ch[i]);
add(ch[i]);
}
build();
ffind(st);
for(int i=;i<=n;i++){
ans=;
ser(ch[i]);
printf("%d\n",ans);
}
return ;
}

指针-AC自动机的更多相关文章

  1. 初学AC自动机

    前言 一直听说\(AC\)自动机是一个很难很难的算法,而且它不在\(NOIP\)提高组范围内(这才是关键),所以我一直没去学. 最近被一些字符串题坑得太惨,于是下定决心去学\(AC\)自动机. 简介 ...

  2. AC自动机 - 关于Fail指针

    fail指针可以说是AC自动机里最难理解的东西,怎样更好的理解AC自动机的fail指针? 先来看一幅图: 看这幅图上的fail指针是怎么构造的. 树上的词分别是: { he , hers , his ...

  3. AC自动机总结及板子(不带指针)

    蒟蒻最近想学个AC自动机简直被网上的板子搞疯了,随便点开一个都是带指针的,然而平时用到指针的时候并不多,看到这些代码也完全是看不懂的状态.只好在大概理解后自己脑补(yy)了一下AC自动机的代码,居然还 ...

  4. hdu 1277 AC自动机入门(指针版和数组版)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1277 推荐一篇博客(看思路就可以,实现用的是java): https://www.cnblogs.co ...

  5. bzoj 2434 AC自动机 + fail指针建树 + 树状数组

    思路:我们先跟着它给定的字符串走把字典树建出来,求出fail指针,我们考虑两个字符串 A和B, 如果想要求B中有多少A的子串,转换一下就是有多少个B的前缀的后缀包含A,这个在AC自动机 的状态图中很容 ...

  6. CF547E Milk and Friends(AC自动机的fail指针上建主席树 或 广义后缀自动机的parent线段树合并)

    What-The-Fatherland is a strange country! All phone numbers there are strings consisting of lowercas ...

  7. ac自动机fail树上按询问建立上跳指针——cf963D

    解法看着吓人,其实就是为了优化ac自动机上暴力跳fail指针.. 另外这题对于复杂度的分析很有学习价值 /* 给定一个母串s,再给定n个询问(k,m) 对于每个询问,求出长度最小的t,使t是s的子串, ...

  8. POJ 1625 Censored!(AC自动机->指针版+DP+大数)题解

    题目:给你n个字母,p个模式串,要你写一个长度为m的串,要求这个串不能包含模式串,问你这样的串最多能写几个 思路:dp+AC自动机应该能看出来,万万没想到这题还要加大数...orz 状态转移方程dp[ ...

  9. AC自动机-算法详解

    What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...

随机推荐

  1. C++ 下啥时候用struct, 啥时候用class

    struct 由c语言引入.在c语言中,是定义结构化数据的标准选择. c++ 同时支持struct 和 class. 原因之一是c++ 是 c 的超集,涵盖c 已支持的语言要素,将更好的支持向下兼容( ...

  2. 039--CSS

    一.CSS定义 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. ''' selector { property: value; property: value; ... propert ...

  3. 025--python初识类和对象

    一.面向对象的定义 说到面向对象,我们先来看一下面向过程的定义:面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么 ...

  4. 任务28:RequestDelegate管道实现思路

    任务28:RequestDelegate管道实现思路 管道的实现机制 RequestDelegate是管道的核心.ApplicationBuilder就是接收了很多个RequestDelegae把它拼 ...

  5. Oracle 查询约束信息

    select * from user_constraints where table_name=''; select * from user_cons_columns;

  6. BZOJ1833(数位dp)

    这个数位dp倒是没什么限制条件,只是需要在过程中把每个数字出现次数记录一下即可.记忆化返回时数学算出.框架还是套板子. #include <cstdio> #include <cma ...

  7. 洛谷p1115 最大子段和

    题目链接: 最大子段和 题目分析: 动态规划O(n)求解,设f[i]表示以i为终点的最大子段和 分两种情况: 若f[i-1]>0,则显然f[i]=f[i-1]+a[i](a[i]必须包含在内) ...

  8. Linux文件和目录的777、755、644权限解释

    Linux文件和目录的权限 1.文件权限 在linux系统中,文件或目录的权限可以分为3种: r:4 读 w:2 写 x:1  执行(运行)-:对应数值0 数字 4 .2 和 1表示读.写.执行权限 ...

  9. 1-13Object类之toString方法

    Object中的toString方法 SUN在Object类中设计toString方法的目的:返回java对象的字符串表示形式. 在现实的开发过程中,Object中的toString方法就是要被重写的 ...

  10. CF 602 D. Lipshitz Sequence 数学 + 单调栈 + 优化

    http://codeforces.com/contest/602/problem/D 这题需要注意到的是,对于三个点(x1, y1)和(x2, y2)和(x3, y3).如果要算出区间[1, 3]的 ...