SAM后缀自动机

本文深入探讨了SAM(Suffix Array Machine)算法的原理与实现,详细解释了有限状态自动机的构成及SAM如何通过增量法识别字符串及其后缀。文章分析了SAM中的边数为何是线性级别,介绍了状态转移、子串集合、以及树形结构的形成,强调了clone步骤在算法中的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

  • 于是有了这篇博客。

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

  • SAMSAMSAM是一个有限状态自动机.

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

  • SAM(x)=TrueSAM(x)=TrueSAM(x)=True表示SAMSAMSAM能识别xxx这个字符串.

  • SAMSAMSAM能识别一个字符串的所有后缀,也能识别子串.

  • trans(s,str)trans(s,str)trans(s,str)表示当前状态是sss,读入字符串strstrstr后到达的状态。为了方便,接下来用ST(str)ST(str)ST(str)表示trans(Init,str)trans(Init,str)trans(Init,str)

  • right(a)right(a)right(a)表示aaa这个字符串在母串sss中每一次出现位置的右端点集合.

  • FacFacFac表示子串集合.

一些姿势
  • s∈Facs\in FacsFac,则ST(s)≠nullST(s)\neq nullST(s)̸=null,否则ST(s)=nullST(s)=nullST(s)=null.

  • a,b∈Fac,right(a)=right(b)a,b\in Fac, right(a)=right(b)a,bFac,right(a)=right(b),则ST(a)=ST(b)ST(a)=ST(b)ST(a)=ST(b).

  • 一个rightrightright集 + 一个长度lenlenlen,唯一确立一个字符串s∈Facs\in FacsFac.

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

  • 状态sss的子串集合中的长度可以表示为[Mins,Maxs][Min_s,Max_s][Mins,Maxs].

  • 两个状态的rightrightrightRa,RbRa,RbRa,Rb只有两种关系,Ra∩RbRa\cap R_bRaRb为空,或Ra⊂RbR_a\subset R_bRaRb.

  • 令一个状态sssfasfa_sfas为满足Right(s)⊂Right(fas)Right(s)\subset Right(fa_s)Right(s)Right(fas)Right(fas)Right(fa_s)Right(fas)最小的状态.

  • 根据fasfa_sfas形成了一个树形结构,且状态数是线性的:

    Proof

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

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

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

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

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

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

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

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

  • trans(Init,S)=lasttrans(Init,S)=lasttrans(Init,S)=last,新建节点npnpnp,则lastlastlast必须向npnpnp连一条ccc树边.

  • 现在来连非树边.

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

  • 一个状态sss有一条标号为xxx的边实际上等价于它的rightrightright集中有rrr满足S[r+1]=xS[r+1]=xS[r+1]=x

  • 沿着fafafa走,直到某个ppp拥有字符为ccc的边,此时再往fafafa走都一定拥有.

  • 判断sonp,cson_{p,c}sonp,cppplenlenlen的关系,并维护好fanpfa_{np}fanp即可.

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值