后缀自动机浅析

第一部分 自动机的预备知识

自动机的功能是识别字符串,一个自动机A,若它能识别字符串S,就记为A(S)=true,否则A(S)=false。 自动机由五个部分组成,字符集alpha,状态集合state,初始状态init,结束状态集合end和状态转移函数trans。 令s,t∈state,ch∈alpha,trans(s,ch)=t表示当前状态是s,在经过一条字符ch的边之后,所到达的状态是t。 如果trans(s,ch)这个转移不存在,那么trans(s,ch)=null。

第二部分 后缀自动机的预备知识

后缀自动机,Suffix Automaton Machine,以下简称SAM,是一个能够识别一个串S的所有后缀的自动机,即对于S的后缀自动机A,设S的长度为n,S[0~n-1],S从i位置开始的后缀为Suffix(i),对于S的任意一个后缀Suffix(i),A(Suffix(i))=true。

定义从SAM的一个状态s能开始能识别的串x,为Reg(s),Reg(s)={x|trans(s,x)∈end}。

定义ST(str)为trans(init,str),即从初始状态输入串str到达的状态。

定义串S的后缀的集合为 Suf,子串的集合为Fac,S[l,r)表示从S的第l个字符到第r-1个字符组成的子串。S下标从0开始。显然,若串x属于Fac,那么ST(x)!=null,设x的位置为[l,r),那么x加上Suffic(r)之后就是S的一个后缀,如果ST(x)=null,那么就不能识别这个后缀了,所以ST(x)!=null。

考虑ST(a)能够识别的字符串,记为Reg(ST(a)),Reg(ST(a))={Suffix(i)|a+Suffix(i)是S的后缀}。所以,Reg(ST(a))是一些后缀的集合。对于串ABBABBBAC ,下标0~8,令a="BBA",我们可以看到a可以在S的多个位置出现。对于一般情况,设a在S中出现的位置集合为

\[\{ [{l_1},{r_1}),[{l_2},{r_2}), \cdots [{l_n},{r_n})\} \]

那么


我们定义Right(a),

\[Right\left( a \right) = \{ {r_1},{r_2}, \cdots {r_n}\} \]

即a串出现的所有结束位置。

对于两个串a,b∈Fac,如果Right(a)=Right(b),那么ST(a)=ST(b),即从初始状态经过a串和经过b串到达的节点相同。反过来,我们可以说,一个状态s,从初始状态到s的路径有多条,对应多个串,这些串的Right集合相同,<strong>也就是说一个状态,表示了多个Right集合相同的串。</strong>

对于串a,令r∈Right(a),再给出一个长度len,那么a=S[r-len,r)。设s=ST(a),我们说了s表示的串有多个(包括a),这些串长度的最大最小值为Max(s),Min(s),那么可以证明,s代表的串的个数有Max(s)-Min(s)+1,而且这些串的长度依次是Min(s),Min(s)+1,...,Max(s)。因为s代表的串都是r位置向前某一段的串,s状态代表的串具有相同的后缀(最长的公共后缀其实就是等于Min(s)的那个串),那么从第r-Min(s)的位置向前,每次加一个字符都是s代表的串,直到到达r-Max(s) ,所以它是连续的。

对于两个状态s1,s2,设他们的Right集合为

\[{R_{s1}},{R_{s2}}\]

设它们有交集,不妨设

\[r \in {R_{s1}} \cap {R_{s2}}\]

由上面那一段的分析,s1和s2代表的串的集合不会有交集,所以[Min(s1),Max(s1)]和[Min(s2),Max(s2)]没有交集(因为它们都是r向前的某一段),不妨设Max(s1)<Min(s2),也就是说s1中的串都比s2中的串的长度小,那么s1中的串必定都是s2中的串的后缀,因为它们都是r位置向前的某个子串。所以

\[{R_{s2}} \subseteq {R_{s1}}\]

所以任意两个Right集合,要么相交,要么一个是另一个的真子集。

第三部分 后缀自动机的线性证明

我们来解释下第二部分最后的那个结论。不妨设串为AABBABD,下标标号0到6,我们给出下面这个表

我们将这个串的所有子串按照Right集合分类,同一类中的子串是Right集合相同的。我们可以看到,它符合第二部分最后的结论:即任意两个Right集合要么无交集,要么一个是另一个的真子集。我们发现,这个性质是一棵树,我们添加一个超级节点S,这个树就是下面这个样子,红色的是节点的Right集合:

我们可以看到,叶子的个数不超过字符串的长度,中间节点至少有两个孩子,所以中间节点的个数也不超过叶子节点的个数,所以节点总数是O(n)的。

还有一个疑问,边的个数是O(n)的吗。可能是吧,不过即使是我也不会证明。

第四部分 后缀自动机的构造方法

我们构造的方法是,首先创建S的一个前缀T的后缀自动机,然后添加一个字符,然后创建串Tx的后缀自动机,即SAM(T)->SAM(Tx)。SAM(Tx)和SAM(T)相比,多了一些子串,这些子串就是Tx的所有后缀。设T的长度为L。考虑T的所有后缀,也就是Right集合包含L的节点

\[{v_1},{v_2}, \cdots {v_k}\]

在这些中存在一个节点p,p=ST(T)。

\[{v_1},{v_2}, \cdots {v_k}\]

都是parent树上p的祖先。首先,用节点np表示ST(Tx),Right(np)={L+1}。显然

\[p = {v_1},{v_2}, \cdots {v_k} = init\]

考虑其中一个v的Right集合,Right(v)={r1,r2,...,rn=L}。如果Right(v)中不存在i使得 S[ri]=x,那么我们直接连一条v到np的x的边即可,因为v代表的串的集合都是T的后缀,加上x都是Tx的后缀。

否则,设$v_p$为$v_1,v_2, \cdots v_k$中第一个有x的出边的节点。

\[Right\left( {{v_p}} \right) = \{ {r_1},{r_2}, \cdots {r_n}\} ,trans({v_p},x) = q\]

那么q的Right集合为

\[Right(q) = \{ {r_i} + 1|S\left[ {{r_i}} \right] = x\} \]

那么现在有两种情况:第一种$Max(q) = Max({v_p}) + 1$,那么直接令np的祖先为q。第二种$Max(q) >Max({v_p}) + 1$,这时候我们新建一个节点nq,$Max(nq) = Max({v_p}) + 1$,nq复制q的孩子信息,原来指向q的边现在连向nq。然后令np的祖先为nq,nq向np连边。

最后我们总结下创建的过程:令当前串为T,加入字符x。令p=ST(T),新建节点np=ST(Tx),对所有p的祖先节点没有x出边的,向np连一条x的边,直到找到一点r使得它有x的出边,若没有这样的r,那么parent(np)=init,结束。否则,令q=trans(r,x),若Max(q)=Max(r)+1,则令parent(np)=q,结束;否则新建节点nq,nq的转移复制q的,parent(nq)=parent(q),parent(q)=parent(np)=nq,并且把r的祖先中出边x到q的,都将出边x连到nq上。

我们给出AABBABD创建SAM的整个过程,

首先,一开始只有一个头,它的长度为0

接着插入第一个字母A


接着插入第二个字母A

接着插入第三个字母B

接着插入第四个字母B

接着插入第5个字母A

接着插入第6个字母B

最后插入D

时间: 2024-08-06 16:05:39

后缀自动机浅析的相关文章

hiho一下第128周 后缀自动机二&#183;重复旋律5

#1445 : 后缀自动机二·重复旋律5 时间限制:10000ms 单点时限:2000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数列. 现在小Hi想知道一部作品中出现了多少不同的旋律? 解题方法提示 输入 共一行,包含一个由小写字母构成的字符串.字符串长度不超过 1000000. 输出 一行一个整数,表示答案. 样例输入 aab 样例输出 5 解题方法提示 小Hi:本周的题目其实就是给定一个字符串S,要求出S的所有不同子串的数

后缀自动机总结

后缀自动机是一种确定性有限自动机(DFA),它可以且仅可以匹配一个给定串的任意后缀. 构造一个可以接受一个给定串的所有后缀的不确定性有限自动机(NFA)是很容易的,我们发现我们用通用的将NFA转换成对应DFA的算法转换出来的DFA的状态数都很小(O(n)级别的,远远达不到指数级别).于是,人们就开始研究这种特殊的NFA,并提出了在线增量算法,用O(n)的时间复杂度构造该NFA的DFA.在转换过程中,DFA中对应的NFA中的状态集合其实就是我们的right集合.——————以上在胡扯———————

BZOJ 2946 Poi2000 公共串 后缀自动机

题目大意:求n个串的最长公共子串 太久没写SAM了真是-- 将第一个串建成后缀自动机,用其它的串进去匹配 每个节点记录每个串在上面匹配的最大长度 那么这个节点对答案的贡献就是所有最大长度的最小值 对所有贡献取最大就行了= = 这最大最小看着真是别扭 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 10100 using namesp

如何优雅的研究 RGSS3 番外(一) ruby 实现的后缀自动机

*我真的不会 ruby 呀* #encoding:utf-8 #============================================================================== # ■ Suffix_Automaton #------------------------------------------------------------------------------ # 后缀自动机. #============================

【BZOJ3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机

[BZOJ3926][Zjoi2015]诸神眷顾的幻想乡 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看.幽香当然也非常高兴啦. 这时幽香发现了一件非常有趣的事情,太阳花田有n块空地.在过去,幽香为了方便,在这n块空地之间修建了n-1条边将它们连通起来.也就是说,这n块空地形成了一个树的结构. 有n个粉丝们来到了太阳花田上.为了表达对幽香生日的祝

【后缀自动机】【拓扑排序】【动态规划】hihocoder1457 后缀自动机四&#183;重复旋律7

解题方法提示 小Hi:我们已经学习了后缀自动机,今天我们再来看这道有意思的题. 小Ho:好!这道题目让我们求的是若干的数字串所有不同子串的和. 小Hi:你能不能结合后缀自动机的性质来思考如何解决本题? 小Ho:这道题目既然是关于子串,那么我知道从后缀自动机的所有状态中包含的子串的集合恰好对应原串的所有不重复子串. 小Hi:很好.那你可以先简化问题,想想只有一个串怎么做? 小Ho:好的.这个难不倒我.我上次已经知道如何计算一个串所有不同子串的数量,现在这题也类似,只不过计算更加复杂一点. 小Hi:

BZOJ3926 ZJOI2015 诸神眷顾的幻想乡 后缀自动机+DFS

题意:给定一颗字符树,求树中路径所构成的不同的字符串的数量,其中AB和BA视作不同的字符串 题解: 题目里有这样一句话:太阳花田的结构比较特殊,只与一个空地相邻的空地数量不超过20个. 一共有10W个点,却只有20个叶子……因此树上所有的字串就是以叶子为起点搜索出的所有字串,丽洁姐真的好善良啊- -(无雾) 这样从每个点开始就能跑出来一颗Trie树,对Trie构造广义后缀自动机——每个节点看成是一个根,在后面加字符的时候和普通的SAM一样. 然后在SAM上用DFS统计不同字串的数量即可 #inc

SPOJ 1812 Longest Common Substring II(后缀自动机)

[题目链接] http://www.spoj.com/problems/LCS2/ [题目大意] 求n个串的最长公共子串 [题解] 对一个串建立后缀自动机,剩余的串在上面跑,保存匹配每个状态的最小值, 取最小值中的最大值即可.由于跑的地方只记录了匹配结尾的状态, 所以还需要更新parent树上的状态,既然匹配到了子节点, 那么parent树链上的值就都能够取到l, 一开始给每个不同状态按照l从小到大分配储存地址, 这样,我们就可以从匹配长度最长的开始更新parent树的情况. [代码] #inc

hdu5853 (后缀自动机)

Problem Jong Hyok and String 题目大意 给你n个字符串,有q个询问. 定义set(s)={(i,j)} 表示 s在第i个字符串中出现,且末尾位置为j. 对于一个询问,求set(Qi)=set(t) ,t的数量. (n,q<=10^5 , 字符串总长<=10^5) 解题分析 直接将n个串塞进一个后缀自动机里面. 对于一个询问串qi,找到其在后缀自动机中出现的位置j. 则答案为len[j] - len[fail[j]] . (具体为什么还需要好好斟酌一下) 参考程序 1