如何优雅地求区间本质不同回文串

如何优雅地求区间本质不同回文串

0. \(O(n^2)\)

直接暴力。一点都不优雅。

1. \(O(n\sqrt n\Sigma)\)

允许双向加的回文树,再套一个(在线)莫队即可。

太拉了,还难写。

2. \(O(n\log^2 n)\)

考虑离线,依次加入字符,维护每个 \(i\) 作为左端点的答案。

考虑维护差分数组,这样每次答案是一段后缀和。

思考一种暴力思路:在回文树上,对于每个串 \(s_j\) 记录最晚出现位置 \(p_j\)

设一次加入后字符串长度为 \(n\),本质上是对于回文树上这个字符串到根路径上所有字符串 \(s_j\),在 \(p_j\)\(-1\),在 \(n-|s_j|\)\(+1\),并将 \(p_j\) 改为 \(n-|s_j|\)

直接做显然会被卡掉。

考虑一个事情,每个回文串的 border 可以表示为 \(O(\log n)\) 个等差数列。注意到一个性质:对于回文串 \(s\),如果存在长度 \(q(q>\frac{|s|}2)\) 的回文前缀,那么一定存在 \(|s|-q\) 的周期。换句话说,如果存在长度为 \(p,q(p>q>\frac{|s|}{2})\) 的回文前缀,那么一定存在 \(2q-p\) 是回文前缀。

所以任意两个等差数列都有前者的首项大于后者的末项的两倍。

并且观察到一个事实:对于等差数列 \(s_i=u^i+s_0\),一定有 \(p_i=-i|u|+p_0\)。换句话说这个字符串的贡献是当前左端点到后一项的左端点。

image

贡献和的并就是最后一个位置为右端点,整个区间上一次出现位置为左端点的区间。

换句话说,我们只需要贡献所有不存在长度 \(>\frac{|s|}2\) 的回文串后缀的后缀 \(s\) 即可。这样的 \(s\) 最多 \(O(\log n)\) 个,具体求出可以直接在回文树上预处理。

复杂度 \(O(n\log^2 n)\)

3. \(O(n\log n)\)

对于任意字符串 \(s\),如果 \(s\) 不是整循环串,那么 \(s\) 的所有循环位移中至多只有 \(2\) 个是回文串。可以想象一个定义在圆上的函数,如果有两条不重合的对称轴,那么一定是一个周期函数。

考虑原串的 Runs \((l,r,p)\),显然 \(s[l:l+p-1]\) 不是整循环串(Runs 的定义),那么至多只有 \(2\)极长整循环周期为 \(p\)的回文串。

可以发现上述算法中,每个极长整循环的回文串只有在循环节处会产生 \(O(\log n)\) 的处理代价。而所有极长整循环的回文串指数之和不超过 Runs 的指数之和的两倍,故是 \(O(n)\) 的。

所以上面那个算法其实是 \(O(n\log n)\) 的。

posted @ 2021-10-17 16:47  Flying2018  阅读(525)  评论(1)    收藏  举报