手玩了一个晚上,终于发现自己的sam爆栈了。。。
@samjia2000 不是玩你233
前言
SAM——suffix aotu-machine
它(Ta?)是有限状态自动机(FSA)的一种(废话)。
它可以实现以O(n)的状态存下一个字符串的所有字串。
因为它把那些本质相同的字串都映射到一个状态上了。
构造
我们记
Rights
表示子串s在原串中出现的结束位置的集合。
很显然
Right
集合要么重合要么无交集。
因为如果有交集那么肯定一个子串是另一个子串的后缀。
于是我们可以设
Parents
表示
Right
集包含了
Rights
且状态最小的那个。
那么我们就构出了一棵parent树。
我们从parent树上往上跳的过程其实就是后缀合并的过程。
那么我们设trans(x,y)表示状态x和字符y之间的转移。
那么每次我们新加入y,新建一个节点np,就可以沿着上一次能够接上y的节点p,如果trans(p,y)=NULL.
那么我们就把它变成np就好了。
当我们第一个找到tran(p,y)=q的时候,这时候分两种情况讨论。
我们设len(x)表示x状态离根节点的最大距离。
1:len(p)+1=len(q)
也就是说状态q一定时候状态p转移过来的。
既然p能够接受状态np,那么q也一定能接受。
于是就把parent(np)=q就好了
2:len(p+1)>len(q)
这时候q就不一定是由状态p转移过来的,但是它肯定包含一些能够接受状态np的状态。
那么我们把这些状态分离出来,设为nq,那么很显然len(nq)=len(p)+1
然后,praent(q)=parent(np)=nq。
因为nq是由q分离出来的,所以显然也可以接受q。
然后,我们就把所有指向状态q并且能够接受状态np的状态都指向nq就好了。
其他状态显然无用。
应用
因为博主是蒟蒻,所以知道的并不多~
比如,求一个串有多少个不同字串。
因为SAM它自动忽略重复字串,所以可以把它看做一个DAG。
也就是求这张图有多少条路径。
直接DP就好了。
如果求字典序第k大/小呢?
那么我们都处理处每一位有多少条路径了,从小到大搜一搜就好了。
余下的。。。有时间在填坑吧~