后缀自动机(知识整理+板子总结)
思路来源
https://www.luogu.com.cn/blog/Kesdiael3/hou-zhui-zi-dong-ji-yang-xie 首推这一篇
https://www.cnblogs.com/zjp-shadow/p/9218214.html 也看了这一篇,也不错
http://hihocoder.com/problemset/problem/1441 hihocoder板子题
https://www.luogu.com.cn/problem/solution/P3804 洛谷板子题
前置知识
自动机的知识(编译原理那一套,比如有限状态自动机云云,知道能在其上沿边转移感觉就可)
AC自动机、Trie树、后缀数组
死记硬背环节
证明和心得(后附)感觉用处不大,直接计入死记硬背和抄板子做题环节
后缀自动机(SAM),是一个最小的确定有限状态自动机(DFA),接受且只接受S的后缀
parent树和自动机节点共用,时间复杂度O(n),空间复杂度O(n)
parent(也称fa,后缀链接link):当一个节点的串,在变为其后缀,且endpos发生了扩大时,最长的那个后缀对应的节点
endpos(也称right):每个节点对应的串的endpos集合是一致的,每个子串唯一出现在某个节点里
len:一个节点内代表的串的最大长度
minlen:一个节点内代表的串的最小长度,由于a回跳到父亲fa(a)的时候len(fa)+1=minlen(a),故一般不存
ch[0-26](后缀转移transfer):自动机上对应字母的转移
配合两张图食用,便于记忆这些概念
图1:abcd的后缀自动机,
红色括号是endpos集合,字母是自动机转移
图二:aaba的后缀自动机,
红色数字是节点对应的最长子串的长度len,蓝边是其parent树上的父亲fa
板子
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct NODE
{int ch[26];int len,fa;NODE(){memset(ch,0,sizeof(ch));len=0;}
}dian[N<<1];
int las=1,tot=1;
void add(int c)
{int p=las;int np=las=++tot;dian[np].len=dian[p].len+1;for(;p&&!dian[p].ch[c];p=dian[p].fa)dian[p].ch[c]=np;if(!p)dian[np].fa=1;//以上为case 1else{int q=dian[p].ch[c];if(dian[q].len==dian[p].len+1)dian[np].fa=q;//以上为case 2else{int nq=++tot;dian[nq]=dian[q];dian[nq].len=dian[p].len+1;dian[q].fa=dian[np].fa=nq; for(;p&&dian[p].ch[c]==q;p=dian[p].fa)dian[p].ch[c]=nq;//以上为case 3}}
}
char s[N];int len;
int main()
{scanf("%s",s);len=strlen(s);for(int i=0;i<len;i++)add(s[i]-'a');
}
性质
①endpos:一个子串在原串中所有出现的地方,其末位置下标的集合
如串abcab,endpos(ab)={2,5}
②endpos相同,一个必为另一个后缀,
考虑abcdbcd,当abcd变成后缀bcd的时候,endpos增加,bcd变成cd的时候,endpos不变
③根据②,任意两个子串的endpos集合,要么一个是另一个子集,要么二者相交为空,
只要有一个相交的位置,说明是后缀,后缀就是含于的情况
④根据②,在子串缩短为其后缀的过程中,要么endpos不变,要么增加,把endpos相同的视为一个等价类节点
在SAM中,一个子串只属于一个节点,这个节点内的子串的endpos都相同
⑤endpos等价类(节点)个数为O(n),
这个没看懂曾经看懂过,可以参考原博客
⑥parent树、自动机
后缀自动机的parent树和自动机的节点是共用的,
parent树往祖先跳的时候,endpos会变大,实际上是当前串变为其后缀串,在节点中用fa定义
自动机就是读进这个字符转移到哪个点,定义了一种转移关系,在节点中用ch[0-26]定义,类似trie
⑦由②,不难发现,endpos不变的一段子串是连续的,
分别记minlen和len为这个点对应的子串的最小长度和最大长度
记点a覆盖的串的长度为[minlen(a),len(a)],则从minlen(a)再缩短一个长度的时候,endpos扩充,跳到父亲
所以有len(fa(a))+1=minlen(a),fa(a)是a在parent树上的父亲
⑧后缀自动机的边数为O(n)
这个没看懂曾经看懂过,可以参考原博客
⑨SAM的构造过程
设当前字母为c,从上一个点las转移过来,当前新开一个点np,endpos多出一个{n}来
先考虑转移,为了最大程度的利用节点,
只对las的祖先节点中,计当前祖先节点为p,
若不存在字母c的转移的点,对其进行转移到np的操作,
并令p回跳到p的父亲,最终p停留在了某个节点
以下分三种情况,实际上是(1)(2)两种情况,其中(2)分为两种子情况
(1)c是没出现过的字符,这样回跳的时候一定会回到根,这里计根为1号节点
根代表了空串,其endpos集合为{1,2,...,n},
因此,c所在节点np构成了一个新的endpos集合{n},直接令fa(np)=1
(2)停留在了中途某个点,此时点p存在字母c的转移,所以停下了,
这其中分成两种子情况,计q为p通过字母c能转移到的点,len[q]是q中最长串的长度
①若len[q]=len[p]+1,由于p是las的祖先,p的串一定是las串的后缀,
np比las多了一个字母c,q比p多了一个字母c,且q最长的串是p最长的串的长度+1,
这表明,在p的串后面补一个字母c,其仍然是 在las串后面补一个字母c 的后缀,
则q的串也是np串的后缀,q的endpos一定是np的endpos的超集,令fa(np)=q即可
②len[q]>len[p]+1,由于len[q]>=len[p]+1(是由p的串补了一个字母c而来),既然不等于,就一定大于
说明q中的串分成了两部分,长度=len[p]+1的那些串x,由①,是np串的后缀,其endpos多出了{n}
长度>len[p]+1的那些串y,不是np串的后缀,其endpos没有变化,
而q是np在往上回跳的过程中,第一个包含了np的所有endpos的集合的点,
既然不能表示,就考虑将q拆成两部分,
一部分是x串集合构成的点,是新开的点,记为nq,有len[nq]=len[p]+1
另一部分是y串集合构成的点,保持原来的q不变,
起先,令nq的所有自动机转移关系(所有ch节点)等于原先的q,这样做可行的原因是:
nq是q的后缀,后续在加入新的字符z时,设其跳到状态nr,
由于nq和q只有碰到字母c时有区别,而z不等于c(设z=c,则nr是第一个包含了np的所有endpos的点,与nq是第一个包含了np的所有endpos集合的点矛盾),
故没有受到新字母c的影响,所以二者唯一的区别endpos{n},没有给后续的nr的状态带来影响
然后考虑fa的关系,nq是旧q的一部分,fa(nq)=fa(q),新q比nq长,可以令fa(q)=nq
构建这个nq节点的意义在于表示np,所以令fa(np)=nq
旧q的儿子(转移关系ch)原封不动保留到了新q和nq中,考虑fa的改变
原先有一些节点指向q,但现在nq成为了q和np的fa,这些点应该指向nq,
所以应从p往其祖先回跳,若其碰到字母c的转移为q,就应将其改为nq,
对于没有通过c转移到q的那些祖先节点,其一定指向了nq的祖先,故不用修改
心得
感觉自己死抠知识点的证明,不做题,没啥大的意义,
有些知识点感性理解一下就好了……
但总之思路来源的博主写的很好,
满足了我这个zz对于原理的一切幻想……
去年8月、11月自学过SAM的原理,但是后来没做题,就渐渐忘了,
现在想想,SA前年当时花了四五天抠原理,但现在也只记得rank、sa、height数组是干啥的了,
实际上,SAM的情况就分三种,代码也很短,掌握了性质就可以了……
证明什么的,除了O(n)的点数和边数的证明略复杂以外,剩下的还好……
没必要每次做题前都复习一下证明,更何况这玩意比SA做题直接很多……
后缀自动机(知识整理+板子总结)相关推荐
- 区间最值(RMQ)的几种方法(知识整理+板子总结)
相当于总结一波,以后就从这里扒RMQ的板子 个人觉得博客的这个粘代码背景没我的黑底白字好看QAQ 代码总是越敲越熟的嘛--知识沉积的过程-- 到时候改一改,传参的时候把数组名一起传进去,就能当小黑盒用 ...
- 莫队算法(知识整理+板子总结)
思路来源 Bzoj-2023 小Z的袜子(hose)_ó-CSDN博客(里面还有几篇不错的莫队讲稿) bzoj2038: [2009国家集训队]小Z的袜子(hose)_thy的专栏-CSDN博客 莫队 ...
- 洛谷-P3975 弦论(后缀自动机板子题)
弦论 一道板子题,让我感觉板子题都还不会...然后把递推写成dfs时没有给string加上&,导致内存爆了,2333,果然还是传引用好 题意:求字典序第K小子串以及本质不同的第K小子串 思路: ...
- 后缀自动机 (WJMZBMR讲稿的整理和注释)
链接放在这里,有点难理解,至少我个人是的. 后缀自动机是一种有限状态自动机,其功能是识别字符串是否是母串的后缀.它能解决的问题当然不仅仅是判断是不是后缀这种事,跟字符串的连续子串有关的问题都可以往这个 ...
- 【数据结构】自动机全家桶(AC、回文、后缀自动机)
自动机全家桶 前言 一.AC自动机 1.优秀博客链接 2.问题模板 3.使用 4.本质 5.运用 6.代码模板 二.回文自动机(回文树) 1.优秀博客链接 2.问题模板 3.使用 4.本质 5.运用 ...
- Spring AOP 知识整理
为什么80%的码农都做不了架构师?>>> AOP知识整理 面向切面编程(AOP)通过提供另外一种思考程序结构的途经来弥补面向对象编程(OOP)的不足.在OOP中模块化的关键单元 ...
- 洛谷P3966 [TJOI2013]单词(后缀自动机)
传送门 统计单词出现次数--为啥大家都是写AC自动机的嘞--明明后缀自动机也能做的说-- 统计出现次数这个就直接按长度排序然后做个dp就好,这是SAM的板子的要求啊,不提了 然后考虑怎么让所有串之间隔 ...
- 算法学习:后缀自动机
[前置知识] AC自动机(没有什么关联,但是看懂了会对后缀自动机有不同的理解) [解决问题] 各种子串的问题 [算法学习] 学习后缀自动机的过程中,看到了许多相关性质和证明,但是奈何才疏学浅(lan) ...
- 【USACO06DEC】—牛奶模式Milk Patterns(后缀自动机)
传送门 后缀自动机板子题了吧 只需要求Parent−TreeParent-TreeParent−Tree上非叶子节点的最长的lenlenlen就可以了 因为字符集太大又不想离散化 就直接用mapmap ...
- BZOJ-2780 Sevenk Love Oimaster(广义后缀自动机)
BZOJ-2780 Sevenk Love Oimaster(广义后缀自动机) 题目链接 题意 给出n个字符串,询问m个串一共出现在几个字符串中. 题解 广义后缀自动机板子题 将n个串建在一起,每次插 ...
最新文章
- radio切换控制div显示_React 项目实践——搭建一个温度控制 App
- python工作-Python工作五年月薪23K,记录一下我的学习经历建议
- iOS 提交应用过程出现的错误及#解决方案#images can't contain alpha channels or transparencies...
- Opencv判断是否加载图片的两种方法
- Spring系列(三):@ComponentScan注解用法介绍
- TCP/IP:IP多播选路
- html5语异性元素,异性的5句性暗示
- CCPC2018(秦皇岛站)赛后反思
- PHP实现简单的计算器
- java ajax 进度条_java使用ajax实现进度条
- html中半透明效果,CSS半透明效果的属性和场景
- 单片机实验板 c语言 打包下载,《AVR单片机开发板 实验板 C语言 视频教程 》
- 《请君入瓮——APT攻防指南之兵不厌诈》—第8章8.5节安全贵在未雨绸缪
- java jemalloc_jemalloc 快速上手攻略
- AdminLTE2的模态框(弹出框)
- AHRS算法代码:磁力计+加计+陀螺版
- vue webapp之music(六)利用axios与后端接口代理请求歌单推荐数据
- 【Ubuntu系列】Ubuntu20.04系统下3060显卡驱动安装记录
- 给你的应用程序添加动态鼠标
- Android常见内存泄漏及优化总结