后缀自动机的应用

零.前置:

(1.init:)初始状态。

(2.end:)结束状态。

(3.E:)结束状态(end)集合。

(4.fa(s):parent)树上(s)的父亲节点。

(5.Reg(s):)节点(s)能达到的(end)的集合。

(6.mx(s):)节点(s)所代表的子串的最长长度。

(7.mn(s):)节点(s)所代表的字串的最短长度。

(8.Right(s):)状态(s)出现的右端点集合。

(9.ST(s):)节点(s)能到达的状态集合(点数)。

一.后缀自动机的性质:

(1.Right(s)subset Right(fa(s)))

(2.mn(s) = mx(fa(s))+1)

(3.s)所代表的所有串在母串中出现的次数和每次出现的右端点相同。

(4.)后缀自动机的(parent)树是原串的反向前缀树(把每个前缀的反串插入到(Trie)树中,并且把没有分支的链合并)。

二.如何求拓扑序:

(exists e(u,v))(mx(u) < mx(v));若(fa(v)=u)也能得到(mx(u) < mx(v))。所以将节点按照(mx)数组排序,可以得到拓扑序(基排(O(n)))。

三.如何求Right(s):

若只求大小,可以按照逆拓扑序递推;如果还需要求具体节点,需要平衡树(启发式合并)/可持久化平衡树;如果需要动态维护(|Reg(s)|)(建立的同时维护),则需要一个数据结构,支持在有根树上加边删边,求子树和,因为有根把子树和转化成链上加减,单点查询然后(LCT)即可。

三.如何求ST(s):

逆拓扑序递推,若求本质不同,则每个状态贡献1;若相同算多次,则每个状态贡献(|Right|)次。

其他应用:

(1.)求本质不同子串数量:(sum_{s}mx(s) - mn(s) + 1)

(2.)求字符串的最小表示:先对(S+S)建立(SAM),然后在(SAM)上跑,每次贪心走字典序最小的出边,走(|S|)步即可。

(3.)求两个字符串的最长公共子串:对一个串建立(SAM),然后跑匹配,如果失配则跳(fa)

(4.)求后缀树和后缀数组:把反串建(SAM)然后(parent)树就是后缀树,后缀树上(dfs)得到后缀数组。

(5.)字典序(k)小子串:求(ST)然后类似线段树二分的去做。

(6.)求本质不同的子串总长:(sum_{s}sum_{i=mn(s)}^{mx(s)}i)

({color{red}{ ext{咕}}}{color{green}{ ext{咕}}}{color{blue}{ ext{咕}}}{color{brown}{ ext{咕}}}{color{gold}{ ext{咕}}}{color{grey}{ ext{咕}}}{color{purple}{ ext{咕}}}{color{pink}{ ext{咕}}}{color{red}{ ext{咕}}}{color{green}{ ext{咕}}}{color{blue}{ ext{咕}}}{color{brown}{ ext{咕}}}{color{gold}{ ext{咕}}}{color{grey}{ ext{咕}}}{color{purple}{ ext{咕}}}{color{pink}{ ext{咕}}})

原文地址:https://www.cnblogs.com/Smeow/p/10698378.html