标签:子串 世界 font 后缀 ext 观察 最大的 目的 sam
第二次做了
回过头来看自己第一次写的代码简直一派胡言
重新看的题解+骚扰Lrefrain解释代码,耗时一整天
然后终于明白了,简直醍醐灌顶
纪念之
给定$S[]$,多次给定$T[],L,R$,询问$T$有多少本质不同的子串是在$S[L...R]$出现过的
$|S|<=5e5 |T|<=1e6$
首先考虑$L==1,R==n$的情况怎么做,
一个直观的想法是拿$T$在$S$的$SAM$上跑
然后沿路打上标记。然而$SAM$上的一个节点代表长度连续的多个串,可能并不能全部跑到
所以要维护一个变量$plen$表示已匹配的长度,每个节点标记上有$min(plen,x->len)-x->f->len$串被跑到了
然后再遍历所有跑过的节点累加就行了?
既然已经注意到每个节点代表的多个子串中,跑到其中一个必定能跑到它的所有后缀
不应该忽略$fail$树上此节点父链上所有的子串都是其后缀,都可以跑到
所以每次都暴力跳父链打上$min(plen,x->len)-x->f->len$的标记,喜提$O(n^2)$
发现跳父链的时候打上的标记全都是$x->len-x->f->len$,即全都包含
则如果发现当前的x已经是这个标记了就不用再跳了,喜提$O(n\sqrt{n})$
为什么这么不优秀,因为被打标记的节点数的上限是和$|S|$有关的,这不好
不如只用$SAM(S)$维护$plen$,同时在$SAM(T)$上跑并打标记,喜提$O(n)$
(其实全世界只有我一开始在$S$上打标记,所有人都想到了同时在$T$上跑。)
然后若$L,R$任意,需要得到$SAM(S[L...R])$,但是不好做
由于$SAM(S)$可以从$root$跑出$S$的所有子串,所以可以仍然依托$SAM(S)$的整体结构
而用$endpos$集合来检查是否符合$[L,R]$的限制
考虑怎么用$endpos$集合来达到等效目的,观察用$SAM(S)$做了什么
1.寻找目前节点$x$有无字符$c$的出边,有就转移过去
设目前完成匹配的子串为$C[1...plen]$,此时的$endpos$为$p$,
若$C[1...plen]+c$仍在$S[L,R]$中,必满足
$p-plen+1\in[L,R]$,$p\in[L,R]$
即$L+plen-1<=p<=R$
检查$x->son[c]$有无在区间$[L+plen,R]$中的$endpos$即可
(由于此时$x=x->son[c],++plen$这行代码还未执行,$plen$还是未加字符$c$前的$plen_0$,不等式中的$plen=plen_0+1$)
2.跳$x$的$parent$
不论是$SAM(S[L...R])$还是整个串的$parent$树
$x$的父链都包含了所有的$x->len$个后缀
而整个串的$SAM$的可能更长一些,但是总的跳父亲次数怎么也不会超过$|T|$
所以完全可以直接使用,不会遗漏任何出现过的最长后缀就完事了
3.查询$x$的$len$
首先思考真的需要查询$x$在$SAM(S[L,R])$的len吗?
答案是否,只想要$T$在$S[L,R]$能维持最长多长的匹配,只是当$L=1,R=n$时,恰好等于$x->len$而已
所以接下来求出来的并不是真正的$len$,而是最长匹配长度。
首先在$[L,R]$中得有$endpos$
考虑其中最大的$endpos$,设此节点所代表的 以这个位置为结尾的子串中 最长的一个为$skyh$
如果$skyh$的左端点$>L$,则在$[L,R]$中的最长长度就等于整个串的$SAM$中这个节点的$len$了
(可以理解为这个$endpos$)
标签:子串 世界 font 后缀 ext 观察 最大的 目的 sam
原文地址:https://www.cnblogs.com/yxsplayxs/p/13080280.html