在北京八十集训的时候就开始学习SAM 直到现在似乎才有一些眉目于是兴起写一发小结

对于证明的话 因为涉及某sjzez大佬版权的问题就不写blog里的内容了 可以私信找我要下大佬blog的内容 (其实和cls的ppt差不多就是更加易懂一点而已orz

设len[x]表示这个节点表示的从初始位置转移过来所能表示的后缀的最长长度 fa[x]表示这个节点状态的par树上的父节点是谁 par树上的父节点可以看作是我当前这个节点表示的right集合的一个更大的集合

如图所示 是一个Par树的图 一个节点的right集合可以看作是所有子树的right 集合的一个并集

以上为基本知识 若要看详细的知识 参见cls在noi2012 wc上的营员交流的ppt 非常全面

怎么实现SAM ?

假设一开始有一个空的SAM考虑如果增加一位 即把字符串T的SAM拓展为Tx的SAM 设p=ST(T) 简单说p是上次加入的节点 新建np=ST(Tx) 连接转移边 trans(p,x)=np 对于p的par树上的所有祖先若没有为x的转移边则 trans(p,x)=np否则找到第一个有转移的祖先vp 若没有vp 则使得fa[np]=root 新建节点结束否则设q=trans(vp,x) 加入len[p]+1==len[q]那么显然直接fa[np]=q状态合并 脱战结束 为什么结束 因为这个时候(right集合中)np是q的子集 直接加入不会使得right集合变小 举例:如q表示ab,np表示aab,vp表示a 拓展结束

否则新建节点nq,将原有q的信息全部转移到nq节点上 并且设定fa[np]=fa[q]=nq len[nq]=len[p]+1这种情况表示当前q的串比vp+1长 显然强行加入会使得right集合改变 所以加入一个更短的节点 并将后继拆成两个独立的节点 如vp表示a  q表示aaab np表示aab那么新建系欸但nq表示ab即可

对于广义的后缀自动机而言就是在一个trie树上建 SAM

参考icefox巨佬的blog 我们可以得到一些清楚的认识:

下面是蒟蒻觉得重要的内容的摘抄:

广义SAM和SAM的一个主要区别就在于插入一个字符之后可能根本就不会产生新的子串可能根本就不用新建节点

什么情况下会发生这种情况?设p=last,要插入字符ch,如果已经存在son[p][ch],记为q,则一定不会产生新的子串(原来在SAM上就可以到达q,所以所有以ch结尾的子串均已存在),对于这种情况我们不能新建np节点!!!那怎么办呢?我们特判一下,如果len[q]==len[p]+1则直接返回q,否则新建nq节点取代q节点(因为虽然没有产生新的子串,但是根据我们对于广义SAM的Right集合的定义(在Trie树上的结束节点),他需要分裂开来。),返回nq。

只有这样构造的广义SAM才是真正正确的。至于为什么就算不特殊处理,构建出了错误的广义SAM也可以达到一样的效果呢:因为我们所谓的错误只不过是生成了一些“无用重复节点”,在parent树上就会出现这样的情况:len[p]==len[fa[p]]。对于一些信息的统计其实是没有影响的,但是如果你要基数排序处理出parent树的拓扑关系,就会出问题,这时你必须让mx相同的节点标号小的排在前面。所以在放回去的时候必须正序处理(orz icefox)。然而这样其实是不正确的,并不优美。

巨佬的原文:https://blog.csdn.net/icefox_zhx/article/details/79704474

蒟蒻的blog 如果有错误请即时联系辣鸡博主qwq 防止祸害他人

 


elijahqi

辣鸡蒟蒻一枚qwq 欢迎加qq qwq 2922945330

发表评论