SAM后缀自动机

Perface
  • 初二时学了一下SAM,当时以为自己懂了,后来仔细琢磨了一番,发现还有许多没有理解的地方。

  • 于是有了这篇博客。

预备
  • 有限状态自动机由五部分组成:alpha字符集,init初始状态,trans转移函数,state状态集合,end结束状态

  • S A M SAM SAM是一个有限状态自动机.

  • 自动机的功能是识别字符串.

  • S A M ( x ) = T r u e SAM(x)=True SAM(x)=True表示 S A M SAM SAM能识别 x x x这个字符串.

  • S A M SAM SAM能识别一个字符串的所有后缀,也能识别子串.

  • t r a n s ( s , s t r ) trans(s,str) trans(s,str)表示当前状态是 s s s,读入字符串 s t r str str后到达的状态。为了方便,接下来用 S T ( s t r ) ST(str) ST(str)表示 t r a n s ( I n i t , s t r ) trans(Init,str) trans(Init,str)

  • r i g h t ( a ) right(a) right(a)表示 a a a这个字符串在母串 s s s中每一次出现位置的右端点集合.

  • F a c Fac Fac表示子串集合.

一些姿势
  • s ∈ F a c s\in Fac sFac,则 S T ( s ) ≠ n u l l ST(s)\neq null ST(s)̸=null,否则 S T ( s ) = n u l l ST(s)=null ST(s)=null.

  • a , b ∈ F a c , r i g h t ( a ) = r i g h t ( b ) a,b\in Fac, right(a)=right(b) a,bFac,right(a)=right(b),则 S T ( a ) = S T ( b ) ST(a)=ST(b) ST(a)=ST(b).

  • 一个 r i g h t right right集 + 一个长度 l e n len len,唯一确立一个字符串 s ∈ F a c s\in Fac sFac.

  • 一个状态 s s s由所有 r i g h t right right集是 r i g h t ( s ) right(s) right(s)的字符串组成,也可以被理解为是一个子串的集合.

  • 状态 s s s的子串集合中的长度可以表示为 [ M i n s , M a x s ] [Min_s,Max_s] [Mins,Maxs].

  • 两个状态的 r i g h t right right R a , R b Ra,Rb Ra,Rb只有两种关系, R a ∩ R b Ra\cap R_b RaRb为空,或 R a ⊂ R b R_a\subset R_b RaRb.

  • 令一个状态 s s s f a s fa_s fas为满足 R i g h t ( s ) ⊂ R i g h t ( f a s ) Right(s)\subset Right(fa_s) Right(s)Right(fas) R i g h t ( f a s ) Right(fa_s) Right(fas)最小的状态.

  • 根据 f a s fa_s fas形成了一个树形结构,且状态数是线性的:

    Proof

    • 因为叶子结点个数是 O ( ∣ S ∣ ) O(|S|) O(S)的.
    • 每个非叶子节点至少有两个孩子,否则可以合并.
    • 所以总节点个数是 O ( ∣ S ∣ ) O(|S|) O(S)的.
  • M a x ( f a s ) = M i n ( s ) − 1 Max(fa_s)=Min(s)-1 Max(fas)=Min(s)1

  • 其实前面都是废话. 接下来是重点。

一些性质
  • 首先我们想证明的是 S A M SAM SAM中的边数是 O ( N ) O(N) O(N)级别的。构造法:

    • 令状态数为 M M M,生成树中的边有 M − 1 M-1 M1条,考虑非树边即可。

    • 生成树中任意 (根到状态 a a a) +( a → b a\rightarrow b ab) +( b b b到结束状态)对应一个后缀。

    • 我们构造每一个后缀只对应一条非树边,且唯一对应。

  • 如何让每一个后缀仅对应一条非树边, S A M SAM SAM采用增量法来构造.

增量法
  • 假设现在已经构造了长为 ∣ S ∣ |S| S S A M SAM SAM,现在要把 c c c字符加进去。

  • t r a n s ( I n i t , S ) = l a s t trans(Init,S)=last trans(Init,S)=last,新建节点 n p np np,则 l a s t last last必须向 n p np np连一条 c c c树边.

  • 现在来连非树边.

  • f a l a s t fa_{last} falast如果没有 c c c字符的边,显然需要连一条非树边,因为 f a l a s t fa_{last} falast ∣ S ∣ |S| S的一个后缀,后缀加上 c c c就是当前的后缀,如果之前没有出现过,必然要连一条非树边。

  • 一个状态 s s s有一条标号为 x x x的边实际上等价于它的 r i g h t right right集中有 r r r满足 S [ r + 1 ] = x S[r+1]=x S[r+1]=x

  • 沿着 f a fa fa走,直到某个 p p p拥有字符为 c c c的边,此时再往 f a fa fa走都一定拥有.

  • 判断 s o n p , c son_{p,c} sonp,c p p p l e n len len的关系,并维护好 f a n p fa_{np} fanp即可.

  • 值得一提的是其中的clone步骤,事实上这也是整个SAM中最神奇的地方。我们可以新建一个节点 n q nq nq来替代 s o n p , c son_{p,c} sonp,c。当然,替代的部分值得深思。我们想要的是 f a n p fa_{np} fanp,所以只有从 p p p直接连向 s o n p , c son_{p,c} sonp,c的这一条边需要转移到 n q nq nq来,然后把 f a n p = f a s o n p , c = n q , f a n q = p fa_{np}=fa_{son_{p,c}}=nq,fa_{nq}=p fanp=fasonp,c=nq,fanq=p即可。需要特别注意的是,如果 f a p fa_{p} fap还有连向 q q q的,都要转移到 n q nq nq来,原因是显然的。

  • 至此完成了整个SAM的构造.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值