AC自动机&后缀自动机
理解的不够深 故只能以此来加深理解 。我这个人就是蠢没办法 学长讲的题全程蒙蔽。可能我字符串就是菜吧,哦不我这个人就是菜吧。
AC自动机的名字 AC 取自一个大牛 而自动机就比较有讲究了 不是寻常的东西呢。
自动机由5部分组成 1 字符集 2 状态集合 3 初始状态 4 结束状态集合 5 状态转移函数。
字符集 是指自动机字符的集合。 当然以上有点深奥,我们只需要其能识别字符串即可。
显然的是 KMP做单字符串对单字符串的匹配使用 而AC自动机则是多个字符串在一个字符串上的匹配。
构建trie 大家都会 但是如何求fail 指针呢 思考一下我们求的fail指针其实是指向和当前这个串能匹配的最长后缀。前缀是没有必要的因为我是利用答案串进行匹配的,因为我们得到一个最长前缀再继续这样匹配还是一样效果我们既然答案串都匹配到了现在 也就是当前局面已经不可逆了我们只能不断的跳 跳到一个合法地方让其能继续匹配下去 所以跳到一个最长后缀的位置这样如果还失配的话我们还有机会继续跳 如果跳到一个较短的后缀上的话我们把那些长的都扔了这显然是非常不好做法。
那么现在就有了基础思路构造fail指针 然后失配就不断跳 就行了。在构造fail指针的时候我们是先构造了一颗trie树在trie树搞fail指针显然最长后缀<当前匹配的位置 至于那些比当前位置还要深的位置一定不可能出现,那么就是看深度咯 bfs逐层扩展的过程中 沿着父亲的fail指针走即可。
//#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 1000000000
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define RI register ll
#define db double
#define EPS 1e-8
using namespace std;
char buf[<<],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf))+fread(buf,,<<,stdin),fs==ft)?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
const int MAXN=;
char a[MAXN];
int n,root,len,cnt,T,h,ans;
int t[MAXN][],e[MAXN];
int q[MAXN],fail[MAXN];
inline void insert(char *c)
{
int p=root;
//cout<<c[1]<<' '<<c[2]<<endl;
for(int i=;i<=len;++i)
{
int w=c[i]-'a';
if(!t[p][w])t[p][w]=++cnt;
p=t[p][w];
}
++e[p];
}
inline void get_fail()
{
T=h=;
for(int i=;i<=;++i)
{
if(t[root][i])
{
q[++T]=t[root][i];
fail[t[root][i]]=;
}
}
while(h++<T)
{
int tn=q[h];
for(int i=;i<=;++i)
{
if(t[tn][i])
{
q[++T]=t[tn][i];
fail[t[tn][i]]=t[fail[tn]][i];
}
else t[tn][i]=t[fail[tn]][i];
}
}
}
inline void AC()
{
int p=root;
for(int i=;i<=len;++i)
{
int c=a[i]-'a';
int w=t[p][c];
p=t[p][c];
while(w&&e[w]!=-)
{
ans+=e[w];
e[w]=-;
w=fail[w];
}
}
}
int main()
{
//freopen("1.in","r",stdin);
n=read();
for(int i=;i<=n;++i)
{
scanf("%s",a+);
len=strlen(a+);
insert(a);
}
get_fail();
scanf("%s",a+);
len=strlen(a+);
AC();
printf("%d",ans);
return ;
}
自己的第二份AC自动机代码这次真的是理解了 因为后缀数组后缀自动机把我搞自闭了。
//#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 1000000000
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define RI register ll
#define db double
#define EPS 1e-8
using namespace std;
char buf[<<],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf))+fread(buf,,<<,stdin),fs==ft)?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
const int MAXN=;
char a[MAXN];
int n,root,len,cnt,T,h;
int t[MAXN][],vis[MAXN];
int q[MAXN],fail[MAXN],ans[MAXN];
inline void insert(char *c,int v)
{
int p=root;
//cout<<c[1]<<' '<<c[2]<<endl;
for(int i=;i<=len;++i)
{
int w=c[i]-'a';
if(!t[p][w])t[p][w]=++cnt;
p=t[p][w];
}
vis[v]=p;
}
inline void get_fail()
{
T=h=;
for(int i=;i<=;++i)
{
if(t[root][i])
{
q[++T]=t[root][i];
fail[t[root][i]]=;
}
}
while(h++<T)
{
int tn=q[h];
for(int i=;i<=;++i)
{
if(t[tn][i])
{
q[++T]=t[tn][i];
fail[t[tn][i]]=t[fail[tn]][i];
}
else t[tn][i]=t[fail[tn]][i];
}
}
}
inline void AC()
{
int p=root;
for(int i=;i<=len;++i)
{
int c=a[i]-'a';
p=t[p][c];++ans[p];
}
for(int i=T;i>=;--i)ans[fail[q[i]]]+=ans[q[i]];
}
int main()
{
freopen("1.in","r",stdin);
n=read();
for(int i=;i<=n;++i)
{
scanf("%s",a+);
len=strlen(a+);
insert(a,i);
}
get_fail();
scanf("%s",a+);
len=strlen(a+);
AC();
for(int i=;i<=n;++i)printf("%d\n",ans[vis[i]]);
return ;
}
发现可以直接AC自动机 然后 树上差分一下即可。
后缀自动机 将所有后缀放到一张图上 满足点的数量级为O(n) 边的数量级为O(n)
关于 点的数量级的证明是从right集处证明的 从right集中我们可以得到一些等价类 而这些等价类可以构成一棵parent的树。
对于一些等价类的节点他们是从一个比自己大的集合之中分裂出来的 故会分裂n-1次每次分裂会的到两个集合 故点最多为2n 数量级为O(n)
考虑这个parent树就已经可以构成一个后缀自动机了,但是连边的方式不太一样。。。所以可以得证 后缀自动机的点的数量级为O(n)
关于边的数量级为O(n)至今还不懂,咕了。
下面考虑AC自动机的构造 这个算法是一个名叫增量算法的东西 一个一个插入字符 这样我们考虑1 只需新建几个节点 2 新建的节点连到什么节点上。
1:第一种是S的第i个前缀·right集合只有i
不知不觉发现咕了???
下面这个 是正常的:
AC自动机&后缀自动机的更多相关文章
- 【专题】字符串专题小结(AC自动机 + 后缀自动机)
AC自动机相关: $fail$树: $fail$树上以最长$border$关系形成父子关系,我们定一个节点对应的串为根到该节点的路径. 对于任意一个非根节点$x$,定$y = fa_{x}$,那$y$ ...
- bzoj 3796: Mushroom追妹纸 AC自动机+后缀自动机+dp
题目大意: 给定三个字符串s1,s2,s3,求一个字符串w满足: w是s1的子串 w是s2的子串 s3不是w的子串 w的长度应尽可能大 题解: 首先我们可以用AC自动机找出s3在s1,s2中出现的位置 ...
- BZOJ2754: [SCOI2012]喵星球上的点名(AC自动机/后缀自动机)
Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串 ...
- BZOJ 3998: [TJOI2015]弦论 后缀自动机 后缀自动机求第k小子串
http://www.lydsy.com/JudgeOnline/problem.php?id=3998 后缀自动机应用的一个模板?需要对len进行一个排序之后再统计每个出现的数量,维护的是以该字符串 ...
- BZOJ4032[HEOI2015]最短不公共子串——序列自动机+后缀自动机+DP+贪心
题目描述 在虐各种最长公共子串.子序列的题虐的不耐烦了之后,你决定反其道而行之. 一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是. 一个串的“子序列”指的是它的可以 ...
- BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机 后缀自动机 字符串
https://www.lydsy.com/JudgeOnline/problem.php?id=3926 广义后缀自动机是一种可以处理好多字符串的一种数据结构(不像后缀自动机只有处理一到两种的时候比 ...
- CCF NOI Online 2021 提高组 T2 积木小赛 (子序列自动机+后缀自动机,O(n^2))
题面 Alice 和 Bob 最近热衷于玩一个游戏--积木小赛. Alice 和 Bob 初始时各有 n 块积木从左至右排成一排,每块积木都被标上了一个英文小写字母. Alice 可以从自己的积木中丢 ...
- HDU - 6208 The Dominator of Strings HDU - 6208 AC自动机 || 后缀自动机
https://vjudge.net/problem/HDU-6208 首先可以知道最长那个串肯定是答案 然后,相当于用n - 1个模式串去匹配这个主串,看看有多少个能匹配. 普通kmp的话,每次都要 ...
- 字符串[未AC](后缀自动机):HEOI 2016 str
超级恶心,先后用set维护right,再用主席树维护,全部超时,本地测是AC的.放心,BZOJ上还是1S限制,貌似只有常数优化到一定境界的人才能AC吧. 总之我是精神胜利了哦耶QAQ #include ...
随机推荐
- css常用的简写技巧_css background简写、css border 简写、css font属性简写等
css样式中有很多简写方式,比如:设置背景,字体,边框,盒子等.我们都可以把css代码合并为一行,这篇文章将总结有哪些属性支持css简写. 1.背景background属性 background-co ...
- 【线型DP】洛谷P2066 机器分配
[线型DP]洛谷P2066 机器分配 标签(空格分隔): 线型DP [题目] 题目描述 总公司拥有高效设备M台,准备分给下属的N个分公司.各分公司若获得这些设备,可以为国家提供一定的盈利.问:如何分配 ...
- Python并发编程——多线程与协程
Pythpn并发编程--多线程与协程 目录 Pythpn并发编程--多线程与协程 1. 进程与线程 1.1 概念上 1.2 多进程与多线程--同时执行多个任务 2. 并发和并行 3. Python多线 ...
- Jmeter系列(43)- 详解 Jmeter 图形化 HTML 压测报告之 Charts 模块
如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html Charts 介绍 包含了各种详细信息 ...
- scrapy 源码解析 (一):启动流程源码分析(一)命令行启动
前言 虽然爬虫的入门级编写并不难,但要让爬虫真正稳定可靠的运行起来,真不是一件容易的事.首先,要用到scrapy,就必须要读懂scrapy这个爬虫框架,如果连这个框架的执行逻辑都搞不懂,那么爬虫也很难 ...
- redis(七):Redis 字符串(String)(python)
# -*- coding: utf-8 -*- import redis #这个redis不能用,请根据自己的需要修改 r =redis.Redis(host="123.516.74.190 ...
- ASP.NET Core端点路由 作用原理
端点路由(Endpoint Routing)最早出现在ASP.NET Core2.2,在ASP.NET Core3.0提升为一等公民. Endpoint Routing的动机 在端点路由出现之前,我们 ...
- Java常用类-Object类
一.java.lang.Object类 1.Object类是所有Java类的根父类 2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类 3.Obj ...
- 当输入一个 URL,实际会发生什么?
从一个经典的面试题说起 从输入URL到页面展现的过程: 输入URL后,会先进行域名解析.优先查找本地host文件有无对应的IP地址,没有的话去本地DNS服务器查找,还不行的话,本地DNS服务器会去找根 ...
- 基于python的自动化测试简介【十年从业大佬】
一.自动化测试包括以下几个方面: 1. 常用测试工具: (1)QTP:主要用于回归测试和测试同一软件的新版本 (2)Robot Framwork:python编写的功能自动化测试框架,具有良好的可扩展 ...