所谓后缀自动机,就是通过后缀建立的自动机

(逃)
请允许我先介绍一下后缀家族:

(又逃)

前言

OI生涯目前为止学习的最为难以理解的算法,没有之一。
到现在也没有完全的理解。
qwq

概念

定义:

后缀 iii :字符串 sss 以 iii 结尾的后缀(前缀同理)
endpos(x)endpos(x)endpos(x) 字符串 xxx 在 sss 中出现的结尾位置的集合
等价类:若 endpos(u)=endpos(v)endpos(u)=endpos(v)endpos(u)=endpos(v),我们就称 uuu 和 vvv 属于同一个等价类

不难发现,对于 sss 的一个子串 x=sl...rx=s_{l...r}x=sl...r​,都存在一个位置 p∈(l,r]p\in (l,r]p∈(l,r],满足对于 l≤i≤pl\le i\le pl≤i≤p,xxx 的后缀 iii 都与 xxx 属于同一个等价类,而对于 p<i≤rp<i\le rp<i≤r,后缀 iii 与 xxx 都不属于一个等价类。
我们称满足这个性质的 ppp 为 link(x)link(x)link(x)。

SAM是一个字符转移边组成的DAG,每条从根结点出发的路径都唯一对应 sss 的一个子串,在SAM中,每个结点都对应着一个等价类的集合(也就是从根结点到该结点的路径所代表的字符串的集合,这些字符串必然由一个最长的字符串和它的一些连续的后缀组成),一个结点的 linklinklink 定义为该结点对应的最长字符串的 linklinklink。

请务必确保你理解了上面这段话

构建

现在考虑如何构建出SAM
使用增量法,当前加入一个新的字符串 ccc
设上一个加入的结点为 lstlstlst,当前加入结点为 curcurcur
设一个结点代表的最长字符串的长度为 lenlenlen

首先,令 len(cur)←len(lst)+1len(cur)\gets len(lst)+1len(cur)←len(lst)+1
然后从 lstlstlst 沿着 linklinklink 不断往上跳,直到跳到某个有 ccc 的转移边或者跳到根为止,沿途把 ccc 的转移边全部赋值成 curcurcur

situation 1

若到根了还没有 ccc 的转移边:说明整个字符串还没有出现过 ccc,直接把 link(cur)link(cur)link(cur) 赋值成根即可

否则,设跳到了结点 ppp,ppp 的 ccc 转移边为 qqq
由于一直跳的是

situation 2

若 len(q)=len(p)+1len(q)=len(p)+1len(q)=len(p)+1:这两个结点在原串上就是相邻的,直接令 link(cur)=link(q)link(cur)=link(q)link(cur)=link(q) 即可

situation 3

若 len(q)≠len(p)+1len(q)\ne len(p)+1len(q)​=len(p)+1:这两个结点在原串上不是相邻的,此时若按照情况2的处理方法,会使SAM上出现不应该出现的前缀,所以我们应该分裂出一个结点 pppppp,继承所有 qqq 的信息,len(pp)←len(p)+1len(pp)\gets len(p)+1len(pp)←len(p)+1,并把 qqq 和 curcurcur 的 linklinklink 全指向 pppppp,再一路往上把本来连向 qqq 的转移连向 pppppp

代码

void ins(int c){c-='a';int cur=++tot,p=lst;lst=tot;st[cur].len=st[p].len+1;siz[cur]=1;for(;p&&!st[p].tr[c];p=st[p].fa) st[p].tr[c]=cur;if(!st[p].tr[c]) st[cur].fa=1;else{int q=st[p].tr[c];if(st[q].len==st[p].len+1) st[cur].fa=q;else{int pp=++tot;st[pp]=st[q];st[pp].len=st[p].len+1;st[q].fa=st[cur].fa=pp;for(;p&&st[p].tr[c]==q;p=st[p].fa) st[p].tr[c]=pp;return;}}
}

应用

求 endpos 集合大小

定义 sizxsiz_xsizx​ 为结点 xxx 的等价类集合中 endposendposendpos 的数目。(也就是出现次数)
那么根据定义,有:
sizx=∑s∈sonxsizs+[x∈S]siz_x=\sum_{s\in son_x} siz_s+[x\in S]sizx​=s∈sonx​∑​sizs​+[x∈S]
其中 SSS 是每次插入的终点集合
dfs或者拓扑实现均可

int cnt[N],id[N];
void calc(){for(int i=1;i<=tot;i++) ++cnt[st[i].len];for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];for(int i=tot;i>=1;i--) id[cnt[st[i].len]--]=i;for(int i=tot;i>=1;i--) siz[st[id[i]].fa]+=siz[id[i]];return;
}

求本质不同子串数

就是在自动机上的走法种类呗。
那么就有:
sumx=∑s=trx,csums+1sum_x=\sum_{s=tr_{x,c}}sum_s+1sumx​=s=trx,c​∑​sums​+1

Thanks for reading!

后缀树

本身也是一个大算法,但是可以通过反串建SAM偷懒。
复杂度为 O(nC)O(nC)O(nC)。

解析

对反串建出后缀自动机,其 failfailfail 树即为所求的后缀树。
设 plxpl_xplx​,为 xxx 节点对应的任意一个出现位置,那么 failx→xfail_x\to xfailx​→x 这条边上对应的字符串就是 s(plx+lenfailx,plx+lenx−1)s(pl_x+len_{fail_x},pl_x+len_x-1)s(plx​+lenfailx​​,plx​+lenx​−1)。
结合 failfailfail 的定义应该不难得到。

后缀树的性质:

  1. 根到叶子的路径和所有后缀一一对应。
  2. 所有非根节点都至少有两个子节点。
  3. LCP(i,j)LCP(i,j)LCP(i,j) 就是两个位置对应节点 lca 的最长长度。

在解决字典序相关问题时较为常用。

模板:后缀自动机(SAM)相关推荐

  1. 后缀自动机(SAM)讲解 + Luogu p3804【模板】后缀自动机 (SAM)

    本文求节点子串长度最小值有点问题,现已修改. SAM 后缀自动机可以存储某一个字符串的所有子串. 一.概念 下图是一个 字符串 "aababa" 的 后缀自动机. 上图中的 黑色边 ...

  2. JZOJ4025. 【佛山市选2015】找回密码(后缀自动机SAM)

    题目描述 Description Kevin是一个热爱字符串的小孩.有一天,他把自己的微信登录密码给忘记了,万般无奈之下只好点"找回密码". 这时候,网页上出现了当初设定的密保问题 ...

  3. 后缀自动机SAM详解

    用一个DFA来识别一个串(比如aabab)的所有后缀,要怎么做呢 最简单的办法,把所有后缀看作要保存的单词,画一棵 trie树,像这样: 点很多很麻烦复杂度也很高 我们给这个DFA按我们的需求合并化简 ...

  4. 多校冲刺NOIP模拟6 - 游戏——矩阵乘法、后缀自动机SAM

    此题不提供链接 题目描述 前言 好久没用SAM了.我记得上次用SAM做题还是在上次. 题解 每一种长度的总方案是确定的,所以我们只需要求出赢的方案数.平局方案数即可. 做法其实和官方正解区别不大,官方 ...

  5. [hdu4416 Good Article Good sentence]后缀自动机SAM

    题意:给出串A和串集合B={B1,B2,...,Bn},求串A的所有不同子串中不是B中任一串的子串的数目. 思路:把A和B中所有字符串依次拼接在一起,然后构造后缀自动机,计算每个状态的R集合元素的最大 ...

  6. P3804 【模板】后缀自动机 (SAM)

    传送门 文章目录 题意: 思路: 题意: 给你一个字符串sss,让你求sss中出现次数不为111的子串出现次数乘上该字串长度最大值. ∣s∣≤1e6|s|\le 1e6∣s∣≤1e6 思路: 没学明白 ...

  7. 【算法竞赛学习笔记】后缀自动机SAM-超经典的字符串问题详解

    title : 后缀自动机 date : 2021-11-11 tags : ACM,字符串 author : Linno 前置知识 KMP,Trie,AC自动机等字符串基础 DFA(有限状态自动机) ...

  8. 后缀自动机 AC自动机

    trie树可遍历出所有无重复后缀,通过后缀遍历前缀可得到所有子串:后缀链接把所有后缀相同的状态(以当前节点为endpos的子串)连接起来,便有了类似KMP的next数组的性质.             ...

  9. 【数据结构】自动机全家桶(AC、回文、后缀自动机)

    自动机全家桶 前言 一.AC自动机 1.优秀博客链接 2.问题模板 3.使用 4.本质 5.运用 6.代码模板 二.回文自动机(回文树) 1.优秀博客链接 2.问题模板 3.使用 4.本质 5.运用 ...

  10. 【牛客 - 551C】CSL 的密码(后缀数组,后缀自动机,随机算法)

    题干: 链接:https://ac.nowcoder.com/acm/contest/551/C 来源:牛客网 为了改变这一点,他决定重新设定一个密码.于是他随机生成了一个很长很长的字符串,并打算选择 ...

最新文章

  1. (C++)1025 PAT Ranking
  2. 他给女朋友做了个树莓派复古相机,算法代码可自己编写,成本不到700元丨开源...
  3. 图文详解什么是快速排序
  4. 提升算法的sklearn-kit的API
  5. 实时监听input输入框value的变化:
  6. html5 拍照 清晰度,html5强大的功能(一)
  7. 信安教程第二版-第8章防火墙技术原理与应用
  8. selenium使用TestNG实现DDT
  9. PAT (Basic Level) Practice1021 个位数统计
  10. mapxtreme for java_在MapXtreme for Java 4.8.0 中公布新制造的电子地图
  11. serverTimezone
  12. matlab中电流继电器,电流电压继电器特性实验的数字仿真
  13. w ndows10输入法设置,Win10输入法设置技巧:切换输入法
  14. 电子凸轮追剪曲线生成算法
  15. 瑞数5维普期刊js逆向
  16. 2021 年最值得推荐的 5 门人工智能编程语言
  17. 生成两组相互独立服从标准正态分布的随机数(推导过程)
  18. java byte与char互转原理_【转】java byte与char互转原理
  19. 成都,今夜请将我遗忘 读后感
  20. The Sandbox 推出 BLOND:ISH 的 ABRACADABRA,一场迷幻的限时音乐会体验

热门文章

  1. 计算机能不能直接识别汇编语言程序,计算机能直接识别执行用汇编语言编写的程序吗...
  2. c语言md5函数 linux,Linux下C语言计算文件的md5值(长度32)
  3. Java开发面试高频考点学习笔记(每日更新)
  4. Java之jdk与jre的区别
  5. mysql 碎片率_MySQL数据碎片的整理和分析
  6. [mybatis]动态sql_sql_抽取可重用的sql片段
  7. [Java基础]自然排序Comparable的使用
  8. C++map容器-构造和赋值
  9. linux把svs文件分割,freeebsd,pkg_add,svsup,make改服务器的设定
  10. php常用linux命令httpd,Linux常用的100个命令