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 ...
随机推荐
- 洛谷 P1640 SCOI2010 连续攻击游戏 并查集
题目描述 lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性.并且每种装备 ...
- React学习路径快速进入AntDesignPro开发
好久没有写博客,有空再来记一下.最近在整些小东西,需要用到前端,最开始本着对nodejs的动不动几百兆插件的恐惧, 于是使用自己以前写的 OSS.Pjax 小框架(类似国外的Pjax,利用pushSt ...
- Centos8 - 图形界面和命令行切换
查看目前默认的启动方式 systemctl get-default 命令行模式:multi-user.target 图形界面模式:graphical.target 设置为图形界面模式 systemct ...
- java 面向对象(二十一):属性的赋值顺序
* ①默认初始化 * ②显式初始化/⑤在代码块中赋值 * ③构造器中初始化 * ④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值 * * ...
- 数据可视化之powerBI基础(二)PowerBI动态图表技巧:钻取交互
https://zhuanlan.zhihu.com/p/64406366 查看可视化图表的时候,我们可能想深入了解某个视觉对象的更详细信息,或者进行更细粒度的分析,比如看到2017年的总体数据,同时 ...
- Centos7之LNMP环境编译安装
Centos7之LNMP环境编译安装 一.系统环境准备 注:安装时间过长,只做参考!!!1.系统信息 [root@localhost ~]# uname -r 3.10.0-957.el7.x86_6 ...
- bzoj3062[Usaco2013 Feb]Taxi*
bzoj3062[Usaco2013 Feb]Taxi 题意: Bessie在农场上为其他奶牛提供出租车服务,她必须赶到这些奶牛的起始位置,并把他们带到它们的目的地.Bessie的车很小,所以她只能一 ...
- bzoj3383[Usaco2004 Open]Cave Cows 4 洞穴里的牛之四*
bzoj3383[Usaco2004 Open]Cave Cows 4 洞穴里的牛之四 题意: 平面直角坐标系有n个点,从(0,0)出发,从一个点上可以跳到所有与它横纵坐标距离都≤2的点上,求最少步数 ...
- [Qt插件]-01Qt插件&&提升部件(自定义控件)
本篇的文档为:How to Create Qt Plugins https://doc.qt.io/qt-5/plugins-howto.html 前言 Qt插件(Qt Plugin)就是一个共享 ...
- LGTB 与 序列
题目描述 LGTB 有一个长度为 N 的序列 A ,现在他想构造一个新的长度为 N 的序列 B ,使得 B 中的任意两个数都互质.并且他要使 \sum_{1\le i\le N}|A_i-B_i| 最 ...