前置芝士
这里面有很多题SA和SAM都可以做,如果有多做法我会简单提一句
后缀数组SA
P4248 [AHOI2013]差异
首先把加减与乘积拆开
前一部分的答案为 ((n-1)sumlimits_{i=1}^n i=dfrac{n(n-1)(n+1)}{2})
后面就是求LCP之和
注意到LCP是height之间最小的那一个,可以单调栈找出以(height_i)为(min)的范围即可
SA求出height即可
SAM做法
前面都是一样的,后面求出子树中节点个数乘一乘即可
P1117 [NOI2016] 优秀的拆分
考虑求出以(i)为结尾的(AA)数量(f_i)与以(i)为开头的(BB)数量(g_i)
答案即为
(f)的求法与(g)类似,这里只考虑求出(g)
枚举(A)的长度(len),每隔(len)标记一个关键点,那么一个长度为(2len)的(BB)一定会覆盖两个关键点。
设两个关键点为(i,j(i<j)),那么(i,j)在一对(AA)里的条件是(LCS(i,j)+LCP(i,j)ge len)
设重叠部分长度为(x=LCS(i,j)+LCP(i,j)-len)
那么对于([i-LCP(i,j)+1,i-LCP(i,j)+1+x])内的点,g都需要(+1)
区间加用差分,LCP,LCS用后缀数组求出
SAM做法
基本一致,只是LCP和LCS用SAM求
P2178 [NOI2015] 品酒大会
与P4248 [AHOI2013]差异类似,单调栈,顺便维护最大值次大值,最小值次小值即可
SAM做法
与P4248 [AHOI2013]差异类似,维护子树中最大值次大值,最小值次小值
bzoj4278. [ONTAK2015]Tasowanie
将两个串拼起来,中间加个(inf),按照rk贪心选择即可
原理就是如果当前位不同,肯定贪心地选小的。否则要依次比较后面哪个能更快选到更小的
后缀自动机SAM
loj6401. yww 与字符串
建出后缀自动机,求出每个节点能向前延伸的最长距离
parent树上DFS,当前节点能延伸的最长长度为(min(maxlimits_{vin subtree_u}val[v],len[u]))
对答案的贡献为(max(0, min(len[u], mx[u]) - len[fa[u]]))
原理就是求用SAM求不同子串数的那个结论
SA做法
参见官方题解
loj6041. 「雅礼集训 2017 Day7」事情的相似度
对于一个节点,对应一个endpos集合,这个endpos集合的LCS至少是(len[u]),也就是我们现在有若干修改,形如((x,y,z)),表示(x,y)之间的LCS为(z)
询问就是求(xge l,yle r)的修改中,(z)的最大值,这个东西可以离线下来二维数点,树状数组维护
现在考虑找出所有修改,暴力找肯定是(O(n^2))的,无法接受。
因为是询问区间([l,r])内的最大值,如果在一个endpos集合中(x<y<z),((x,y),(y,z),(x,z))都可以做出贡献,但是((x,z))的贡献是没有影响的。使用set启发式合并,每次只增加相邻的位置的贡献,复杂度(O(nlog^2 n))
P3346 [ZJOI2015]诸神眷顾的幻想乡
树上的一条链一定是以叶子为根的树中一条儿子到祖先的链,因为叶子节点数很少,也就相当于20个Trie。对每一个叶子DFS一下,建出广义后缀自动机求(sum len[fa[i]]-len[i])即可
CF666E Forensic Examination
建出广义SAM,在后缀自动机上找出(S[1,cdots,i])对应的节点(pos[i]),与能够匹配的最大长度(mxlen[i])
找(S(l,r))就是在parent树上倍增,找到(len_uge r-l+1)的深度最小的点(u),这样我们就找到了(S(l,r))对应的节点(子树)
开一个线段树,维护每个节点的endpos中,串编号在([l,r])内的最大出现次数和对应的串,这个可以线段树合并做到(O(nlog n))
找到节点之后询问即可