AC 自动机查漏补缺

推荐在 cnblogs 上阅读

AC 自动机查漏补缺

前言

今年 1 月份学过一次,当时自以为掌握得很好,实际上就是依托答辩。而且还有很多地方是有严重误导性的。所以这篇查漏补缺就是记录一下自己对 AC 自动机尚不完全掌握的地方。并对之前的那篇不太正确的题解进行纠正。

因此,在这样的背景下,这篇文章注定就不是给初学者看的,是大致了解了 AC 自动机是什么后的一篇提高读物。

当然你硬要初学就来看也没问题,毕竟此篇文章与重头开始写没有什么区别。

三问 AC 自动机

第一问:AC 自动机到底是什么?

KMP 是解决单模式串匹配问题的算法,而 AC 自动机则可以解决多模式串匹配问题。

第二问:AC 自动机靠什么实现?

通过多模式串建起一棵 Trie 树,再构建 Trie 上每个点的失配指针(fail),由此可得 Fail 树。将文本串在 Trie 上留痕。然后就可以得到文本串前缀的信息。根据 Fail 树的定义可以更新这些前缀的后缀,即文本串的子串。同时如果该子串如果是模式串则可以维护模式串的信息了。

第三问:AC 自动机为什么叫“自动机”?

自动机是一种数学模型,至于它的深层的定义可以去看《自动机 - OI Wiki》。

三问 Fail 树

第一问:Fail 树是怎么冒出来的?

AC 自动机一个重要的步骤是构建失配指针,即 fail 指针。某个点的 fail 指针所指向的点在 Trie 上对应的字符串是该点在 Trie 上对应的字符串的最大后缀(“对应的字符串”指 Trie 上的根节点到此点的路径上的字符构成的串)。所以每个点都只有一个 fail 指针(可能指向根节点),每个点也有可能被多个节点的失配指针指向。这个就很像父子关系,所以 fail 指针构造出来后,即得到了一棵 Fail 树。

第二问:Fail 树和 Trie 树有什么区别?

Fail 树上每个节点的祖先都是该点在 Trie 上对应字符串的后缀。Trie 树是把每个模式串塞进去而建起来的。当文本串在 Trie 上留痕后,一个留痕过的点从根节点到它的路径上形成的字符串就是文本串的前缀(这个很显然啦~)。

第三问:Fail 树和 Trie 树有什么联系?

首先是在 Trie 上将文本串留痕,边留痕边在该节点记录信息。这样结束后便得到了文本串前缀的信息。我们还想处理文本串子串的信息。这时候需要搬出我们的 Fail 树。拓扑排序,从叶子节点将信息传递到父亲节点。为什么呢?因为 Fail 树上的祖先是该点的后缀,而前缀的后缀就是子串。为什么可以这样?因为该点是文本串留痕过的点,说明此时是可以匹配上文本串的。既然该点的串可以匹配上,那该点的串的后缀必然也是可以匹配上的。

典型例题

P3808 AC 自动机(简单版)

#include<bits/stdc++.h>
using namespace std;#define int long longconst int N=1e6+5,M=30;int n;
char str[N],s[N];
int tr[N][M],fail[N],tot,deg[N],pos[N];
bool vis[N];void ins(char *s,int j)
{int len=strlen(s),root=0;for(int i=0;i<len;i++){if(!tr[root][s[i]-'a'])tr[root][s[i]-'a']=++tot;root=tr[root][s[i]-'a'];}pos[j]=root;
}void FL()
{queue<int> q;for(int i=0;i<26;i++)if(tr[0][i])q.push(tr[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0;i<26;i++){if(tr[u][i]){fail[tr[u][i]]=tr[fail[u]][i];q.push(tr[u][i]);}elsetr[u][i]=tr[fail[u]][i];}}
}void AC()
{int len=strlen(str),root=0;for(int i=0;i<len;i++){root=tr[root][str[i]-'a'];vis[root]=1;}for(int i=1;i<=tot;i++)deg[fail[i]]++;queue<int> q;for(int i=1;i<=tot;i++)if(!deg[i])q.push(i);while(!q.empty()){int u=q.front();q.pop();if(!u) continue;vis[fail[u]]=1;deg[fail[u]]--;if(!deg[fail[u]])q.push(fail[u]);}
}signed main()
{scanf("%lld",&n);for(int i=1;i<=n;i++){scanf("%s",s);ins(s,i);}scanf("%s",str);FL();AC();int ans=0;for(int i=1;i<=n;i++)if(vis[pos[i]])++ans;printf("%lld\n",ans);return 0;
}

P3796 AC 自动机(简单版 II)

#include<bits/stdc++.h>
using namespace std;#define int long longconst int N=1e6+5,M=30,L=80;int n;
char str[N],s[N][L];
int tr[20000][M],fail[N],tot,pos[N];
int vis[N];void ins(char *s,int j)
{int len=strlen(s),root=0;for(int i=0;i<len;i++){if(!tr[root][s[i]-'a'])tr[root][s[i]-'a']=++tot;root=tr[root][s[i]-'a'];}pos[tot]=j;
}void FL()
{queue<int> q;for(int i=0;i<26;i++)if(tr[0][i])q.push(tr[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0;i<26;i++){if(tr[u][i]){fail[tr[u][i]]=tr[fail[u]][i];q.push(tr[u][i]);}elsetr[u][i]=tr[fail[u]][i];}}
}void AC()
{int len=strlen(str),root=0;for(int i=1;i<=tot;i++)vis[i]=0;for(int i=0;i<len;i++){root=tr[root][str[i]-'a'];for(int j=root;j;j=fail[j])vis[pos[j]]++;}
}signed main()
{while(~scanf("%lld",&n)&&n){memset(fail,0,sizeof(fail));memset(tr,0,sizeof(tr));memset(pos,0,sizeof(pos));tot=0;for(int i=1;i<=n;i++){scanf("%s",s[i]);ins(s[i],i);}
//		cerr<<tot<<"\n";scanf("%s",str);FL();AC();int ans=0;for(int i=1;i<=n;i++)ans=max(ans,vis[i]);printf("%lld\n",ans);for(int i=1;i<=n;i++)if(vis[i]==ans)puts(s[i]);}return 0;
}

P5357 【模板】AC 自动机

#include<bits/stdc++.h>
using namespace std;#define int long longconst int N=2e5+5,M=30,L=80;int n;
char str[N*10],s[N][L];
int tr[N][M],fail[N],tot,deg[N],pos[N];
int vis[N];void ins(char *s,int j)
{int len=strlen(s),root=0;for(int i=0;i<len;i++){if(!tr[root][s[i]-'a'])tr[root][s[i]-'a']=++tot;root=tr[root][s[i]-'a'];}pos[j]=root;
}void FL()
{queue<int> q;for(int i=0;i<26;i++)if(tr[0][i])q.push(tr[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0;i<26;i++){if(tr[u][i]){fail[tr[u][i]]=tr[fail[u]][i];q.push(tr[u][i]);}elsetr[u][i]=tr[fail[u]][i];}}
}void AC()
{int len=strlen(str),root=0;for(int i=0;i<len;i++){root=tr[root][str[i]-'a'];vis[root]++;}queue<int> q;for(int i=1;i<=tot;i++)deg[fail[i]]++;for(int i=1;i<=tot;i++)if(!deg[i])q.push(i);while(!q.empty()){int u=q.front();q.pop();if(!u) continue;vis[fail[u]]+=vis[u];deg[fail[u]]--;if(!deg[fail[u]])q.push(fail[u]);}
}signed main()
{scanf("%lld",&n);for(int i=1;i<=n;i++){scanf("%s",s[i]);ins(s[i],i);}scanf("%s",str);FL();AC();int ans=0;for(int i=1;i<=n;i++)printf("%lld\n",vis[pos[i]]);return 0;
}

后记

感觉例题没什么好说的,这篇文章本来就不是详解,只是一点查漏补缺。代码都是在写文章当天重写过一遍的,以此来加深印象。

另:之前那篇《浅谈 AC 自动机》有些有错误的地方,例如 fail 指针的构建根本就不是路径压缩,而是记忆化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/52074.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决电脑突然断网没网,以太网无网络访问权限,本地连接时出错:地址仍未与网络终结点关联

帮同事处理网络问题&#xff0c;尝试了拔插网线&#xff0c;重启电脑&#xff0c;禁用启用以太网&#xff0c;都没有解决。 于是在**命令提示符(cmd)**中执行命令&#xff1a; ipconfig /release 按回车执行后&#xff0c;返回提示&#xff1a;本地连接时出错&#xff1a;地址…

什么是GD32 MCU读保护?

如今电子产品市场风云变幻&#xff0c;暗流汹涌&#xff0c;有没有小伙伴遇到自己费了大力气写出来的代码&#xff0c;很容易就被别人“借鉴”了&#xff0c;真的是让闻者伤心&#xff0c;听着落泪啊。 那有没有什么方法可以防止别人将你的代码从MCU读出来呢&#xff1f;答案当…

大众点评2024年6月全国全分类店铺基础信息数据库

大众点评的采集在2023年之前还是比较好采集的&#xff0c;很多接口不需要登录&#xff0c;即使登录一个帐号也可以采集很多&#xff0c;所以大约2023年8月以前的大众点评店铺字段非常丰富&#xff0c;几乎所有常见店铺字段都能采集。 2023年8月以后&#xff0c;大量接口权限变…

远程消息传递的艺术:NSDistantObject在Objective-C中的妙用

标题&#xff1a;远程消息传递的艺术&#xff1a;NSDistantObject在Objective-C中的妙用 引言 在Objective-C的丰富生态中&#xff0c;NSDistantObject扮演着至关重要的角色&#xff0c;特别是在处理分布式系统中的远程消息传递。它允许对象之间跨越不同地址空间进行通信&…

网安新声 | 网易云音乐崩了:网络安全如何守护在线体验

网安加社区【网安新声】栏目&#xff0c;汇聚网络安全领域的权威专家与资深学者&#xff0c;紧跟当下热点安全事件、剖析前沿技术动态及政策导向&#xff0c;以专业视野和前瞻洞察&#xff0c;引领行业共同探讨并应对新挑战的策略与可行路径。 8月19日&#xff0c;#网易云音乐崩…

企业高性能web服务器【Nginx详解】

一.Web 服务基础介绍 1.1 互联网发展历程 1993年3月2日&#xff0c;中国科学院高能物理研究所租用AT&T公司的国际卫星信道建立的接入美国SLAC国家实 验室的64K专线正式开通&#xff0c;成为我国连入Internet的第一根专线。 1995年马云开始创业并推出了一个web网站 中国黄页…

VAuditDemo安装漏洞

目录 VAuditDemo安装漏洞 index.php header.php config.php lib.php install.php 分析结果 漏洞利用 第一步&#xff1a;删除install.lock文件&#xff0c;访问 install.php 抓包 第二步&#xff1a;通过审计构造payload 第三步&#xff1a;修改抓包请求内容&#x…

异常在代码中的两个作用

一.异常的作用: 作用一:异常是用来查询bug的关键参考信息。 作用二:异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况。 二.举例: 例1: 一个JavaBean类: package com.itheima.a01MyExpection;public class Student { private String name; private int…

【极限性能,尽在掌控】ROG NUC:游戏与创作的微型巨擘

初见ROG NUC&#xff0c;你或许会为它的小巧体型惊讶。然而&#xff0c;这看似不起眼的机身内&#xff0c;蕴藏着游戏、创意的强大能量。 掌中风暴&#xff0c;性能无界 ROG NUC搭载英特尔高性能处理器&#xff0c;配合高速NVMe SSD固态硬盘以及可选的高端独立显卡&#xff08…

“解决Windows电脑无法投影到其他屏幕的问题:尝试更新驱动程序或更换视频卡“

目录 背景: 解决方法1: 解决方法2: 什么是驱动程序&#xff1a; 背景: 今天在日常的工作中&#xff0c; 我想将笔记本分屏到另一个显示屏&#xff0c;我这电脑Windows10系统&#xff0c;当我按下Windows键P键&#xff0c;屏幕信息上提示我"你的电脑不能投影到其他屏幕…

mybatis-plus使用saveOrUpdateBatch函数时数据库中已存在对应id数据,但报错插入时出现重复键

1. 问题背景 ProgramLang pl4 new ProgramLang(); // pl4.setId(100L).setLangName("YY").setDescription("Drama2");pl4.setId(100L);pl4.setLangName("YY");pl4.setDescription("Drama2");List<ProgramLang> updatedE…

调研在深度学习中如何读代码

这里调研了四个up主的内容&#xff0c;对他们讲的内容摘了一下主要的内容。想要看原文的画可以看原篇。 1.如何学习别人的代码&#xff08;代码量较大时&#xff09;_怎么学习别人的代码-CSDN博客 想要掌握的好&#xff0c;光阅读是不够的&#xff0c;一定要动手写、训练模型…

STM32学习9

USART串口协议 通信接口 通信的目的&#xff1a;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统 通信协议&#xff1a;制定通信的规则&#xff0c;通信双方按照协议规则进行数据收发 串口通信 串口是一种应用十分广泛的通讯接口&#xff0c;串口成本低、容易使…

python 可迭代,迭代器,生成器,装饰器

1. 可迭代&#xff08;Iterable&#xff09; 可迭代 是指一个对象可以返回一个迭代器的对象。也就是说&#xff0c;它实现了 __iter__() 方法或 __getitem__() 方法。常见的可迭代对象有列表、元组、字符串、字典和集合。 from collections.abc import Iterablei 100 s &qu…

echo “Hello, UDP!“ | nc -u -w1 192.168.1.100 1234 里面有换行符

当你使用echo命令时&#xff0c;默认情况下会包含一个换行符&#xff08;\n&#xff09;&#xff0c;这可能会导致你的UDP数据包包含额外的字符。如果你想确保发送的数据不包含换行符&#xff0c;可以使用printf命令&#xff0c;因为它允许你更精确地控制输出的内容。 下面是修…

设计模式-visit模式-在语法树的实践

文章目录 背景示例代码分析灵活性双重分派 总结 背景 很多项目代码有accept()用法&#xff0c;在calcite 里也看到了这种&#xff0c;深入了解一下 语法树遍历&#xff1a;编译器通常会将源代码解析成抽象语法树&#xff08;AST&#xff09;。为了实现不同的编译阶段&#xff…

[Qt][Qt 文件]详细讲解

目录 1.输入输出设备类2.文件读写类3.文件和目录信息类 1.输入输出设备类 在Qt中&#xff0c;⽂件读写的类为QFile&#xff0c;其⽗类为QFileDevice QFileDevice提供了⽂件交互操作的底层功能QFileDevice的⽗类是QIODevice&#xff0c;其⽗类为QObject QIODevice是Qt中所有I/O…

统一待办集成方案:优化工作流,实现高效协作

在现代企业中&#xff0c;待办事项的管理往往分散在多个系统和工具中&#xff0c;这不仅导致信息孤岛&#xff0c;还可能影响工作效率和协作效果。为了解决这些问题&#xff0c;统一待办集成方案应运而生&#xff0c;它通过整合不同的待办事项管理系统&#xff0c;实现统一的任…

springboot密码加密步骤

1.添加依赖&#xff1a; <!--密码加密 --> <dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-boot-starter</artifactId><version>${jasypt.version}</version> </dependency> 2.配…

做无效私域,比不做还可怕!

这几年&#xff0c;市场上几乎80%的企业都在做同一件事&#xff1a;“私域”营销。 公众号、私域社群、企业微信、视频号……大家用的工具和平台都差不多&#xff0c;但运营效果却是天差地别。为什么很多企业的私域都做不起来&#xff0c;有的企业却做的风生水起&#xff0c;今…