理解的不够深 故只能以此来加深理解 。我这个人就是蠢没办法 学长讲的题全程蒙蔽。可能我字符串就是菜吧,哦不我这个人就是菜吧。

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自动机&后缀自动机的更多相关文章

  1. 【专题】字符串专题小结(AC自动机 + 后缀自动机)

    AC自动机相关: $fail$树: $fail$树上以最长$border$关系形成父子关系,我们定一个节点对应的串为根到该节点的路径. 对于任意一个非根节点$x$,定$y = fa_{x}$,那$y$ ...

  2. bzoj 3796: Mushroom追妹纸 AC自动机+后缀自动机+dp

    题目大意: 给定三个字符串s1,s2,s3,求一个字符串w满足: w是s1的子串 w是s2的子串 s3不是w的子串 w的长度应尽可能大 题解: 首先我们可以用AC自动机找出s3在s1,s2中出现的位置 ...

  3. BZOJ2754: [SCOI2012]喵星球上的点名(AC自动机/后缀自动机)

    Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串 ...

  4. BZOJ 3998: [TJOI2015]弦论 后缀自动机 后缀自动机求第k小子串

    http://www.lydsy.com/JudgeOnline/problem.php?id=3998 后缀自动机应用的一个模板?需要对len进行一个排序之后再统计每个出现的数量,维护的是以该字符串 ...

  5. BZOJ4032[HEOI2015]最短不公共子串——序列自动机+后缀自动机+DP+贪心

    题目描述 在虐各种最长公共子串.子序列的题虐的不耐烦了之后,你决定反其道而行之. 一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是. 一个串的“子序列”指的是它的可以 ...

  6. BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机 后缀自动机 字符串

    https://www.lydsy.com/JudgeOnline/problem.php?id=3926 广义后缀自动机是一种可以处理好多字符串的一种数据结构(不像后缀自动机只有处理一到两种的时候比 ...

  7. CCF NOI Online 2021 提高组 T2 积木小赛 (子序列自动机+后缀自动机,O(n^2))

    题面 Alice 和 Bob 最近热衷于玩一个游戏--积木小赛. Alice 和 Bob 初始时各有 n 块积木从左至右排成一排,每块积木都被标上了一个英文小写字母. Alice 可以从自己的积木中丢 ...

  8. HDU - 6208 The Dominator of Strings HDU - 6208 AC自动机 || 后缀自动机

    https://vjudge.net/problem/HDU-6208 首先可以知道最长那个串肯定是答案 然后,相当于用n - 1个模式串去匹配这个主串,看看有多少个能匹配. 普通kmp的话,每次都要 ...

  9. 字符串[未AC](后缀自动机):HEOI 2016 str

    超级恶心,先后用set维护right,再用主席树维护,全部超时,本地测是AC的.放心,BZOJ上还是1S限制,貌似只有常数优化到一定境界的人才能AC吧. 总之我是精神胜利了哦耶QAQ #include ...

随机推荐

  1. LESS 原理,一款css的预处理程序Less的使用

    ​Less一种动态样式语言,LESS将CSS赋予了动态语言的特性,如变量,继承,运算,函数...LESS 既可以在客户端上运行 (支持IE 6+, Webkit, Firefox),也可以借助Node ...

  2. [USACO16OPEN]248 G——区间dp

    [USACO16OPEN]248 G 题目描述 Bessie likes downloading games to play on her cell phone, even though she do ...

  3. CF3D Least Cost Bracket Sequence 题解

    题目 This is yet another problem on regular bracket sequences. A bracket sequence is called regular, i ...

  4. C++各种格式转换

    int main() {     stringstream sstr;     //--------int转string-----------     int a=100;     string st ...

  5. day49 数据库终章

    目录 一.pymysql补充 二.数据库补充 1 视图(了解) 2 触发器(了解) 3 事务 4 存储过程(了解) 5 函数 6 流程控制 7 索引 8 b+树 9 聚集索引(primary key) ...

  6. Let's GO(一)

    近来开始学Go,留此博客以记录学习过程,顺便鞭策自己更加努力. 人生苦短,Let's GO! Let's GO(一) Let's GO(二) Let's GO(三) Let's GO(四) 简单介绍 ...

  7. CTF_show平台 web题解 part3

    web13 题目显示文件上传,各类型上传均提示错误,在使用ctf-scan扫描的时候,发现upload.php.bak. 查看源码: <?php header("content-typ ...

  8. Java并发编程实践

    最近阅读了<Java并发编程实践>这本书,总结了一下几个相关的知识点. 线程安全 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任 ...

  9. Dubbo测试环境服务调用隔离这么玩对么

    背景阐述 前几天,有位同学问我一个关于 Dubbo 的问题.他的诉求是这样子的: 诉求一 第一个诉求是本地开发的时候想自己调用自己的服务,比如自己在改 A 服务,然后出问题了,本地再启动一个 B 服务 ...

  10. Ethical Hacking - GAINING ACCESS(21)

    CLIENT SIDE ATTACKS - Trojan delivery method - using email spoofing Use gathered info to contract ta ...