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

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. 「树形DP」洛谷P2607 [ZJOI2008]骑士

    P2607 [ZJOI2008]骑士 题面: 题目描述 Z 国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬. 最近发生了一件可怕的事情,邪恶的 ...

  2. GoLand 2020.1.3破解教程

    此教程适用于GoLand 2020.1.3其他版本理论上是一样的. GoLand建议从官网下载---->>>>>>下载 1 如果之前有激活留下的补丁文件,建议卸载删 ...

  3. 在Linux下安装zotero

    背景 系统:deepin15 zotero5.0 本来deepin的商店里是有zotero的,但貌似商店里的太老了,安装完打开之后什么功能都不能用,点击按钮没有反应.无奈之下,只能手动安装了 网上的教 ...

  4. DVWA学习记录 PartⅨ

    XSS(DOM) 1. 题目 XSS,全称Cross Site Scripting,即跨站脚本攻击,某种意义上也是一种注入攻击,是指攻击者在页面中注入恶意的脚本代码,当受害者访问该页面时,恶意代码会在 ...

  5. Centos7 python 安装 Ignoring ensurepip failure: pip 9.0.1 requires SSL/TLS

    安装python时出现Ignoring ensurepip failure:pip required SSL/TLS 因为没有安装OpenSSL 使用yum install openssl-devel

  6. Django -MD5密码加密与登录

    直接贴代码 login_reg.py from django.shortcuts import render, redirect from web.forms.login_reg import Reg ...

  7. 数据可视化之powerBI技巧(十一)基于SQL思维的PowerBI DAX实战

    本文来自于PowerBI星球嘉宾天行老师的分享,天行老师不仅DAX使用娴熟,更是精通SQL,下面就来欣赏他利用SQL思维编写DAX解决问题的一个实战案例. 基于SQL思维使用DAX解决实战问题 作者: ...

  8. 访问控制列表与SSH结合使用,为网络设备保驾护航,提高安全性

    通过之前的文章简单介绍了华为交换机如何配置SSH远程登录,在一些工作场景,需要特定的IP地址段能够SSH远程访问和管理网络设备,这样又需要怎么配置呢?下面通过一个简单的案例带着大家去了解一下. 要实现 ...

  9. tomcat内容总结

    tomcat的安装以及配置环境变量 1.tomcat的官网下载地址:http://tomcat.apache.org/ tomcat有很多版本,有解压版 和 安装版,还分windows (还分为32位 ...

  10. Java面试题汇总(持续更新)

    1. ==和equals的区别 答: 基础数据类型比较:只能使用==,比较值是否相等 引用数据类型比较: 没有重写equals方法:==和equals没有区别,比较的都是引用是否指向了同一块内存 重写 ...