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 Facs∈Fac,则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,b∈Fac,right(a)=right(b),则ST(a)=ST(b)ST(a)=ST(b)ST(a)=ST(b).
-
一个rightrightright集 + 一个长度lenlenlen,唯一确立一个字符串s∈Facs\in Facs∈Fac.
-
一个状态sss由所有rightrightright集是right(s)right(s)right(s)的字符串组成,也可以被理解为是一个子串的集合.
-
状态sss的子串集合中的长度可以表示为[Mins,Maxs][Min_s,Max_s][Mins,Maxs].
-
两个状态的rightrightright集Ra,RbRa,RbRa,Rb只有两种关系,Ra∩RbRa\cap R_bRa∩Rb为空,或Ra⊂RbR_a\subset R_bRa⊂Rb.
-
令一个状态sss的fasfa_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-1M−1条,考虑非树边即可。
-
生成树中任意 (根到状态aaa) +( a→ba\rightarrow ba→b) +( bbb到结束状态)对应一个后缀。
-
我们构造每一个后缀只对应一条非树边,且唯一对应。
-
-
如何让每一个后缀仅对应一条非树边,SAMSAMSAM采用增量法来构造.
增量法
-
假设现在已经构造了长为∣S∣|S|∣S∣的SAMSAMSAM,现在要把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,c与ppp的lenlenlen的关系,并维护好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的构造.