【学习笔记】Palindrome Series
SoyTony 近日每天给我讲字符串,我听的受不了了,所以我要来写字符串的博。
前情提要:全是感情,没有证明。
bikuhiku 说我不应该使用“前情提要”这个词,我想了想感觉很对,那换一个词。
前情提要:全是感情,没有证明。
Border Series
Border 有一个性质:对于一个长为 \(n\) 的字符串,其所有长度大于等于 \(\frac{n}{2}\) 的 Border 构成等差数列。
感性理解下,如果大于 \(\frac{n}{2}\),那么前后缀的交集就又是一个 Border。
那么由此可以推出一个性质:一个字符串的 Border 可以划分为 \(\log n\) 段等差序列。
这就是 Border Series。
不知道有啥用,如果有人有例题麻烦给我一个。
Palindrome Series
众所周知,回文串有着与 Border 类似的性质,于是上面的性质可以直接搬过来用。
也就是,对于一个回文串,它的回文串后缀的长度也可以划分为 \(\log n\) 段等差序列。
然后它可以用来解决一些回文串 DP 的问题。
这类问题一般转移式子长这样:
其中 \(P\) 为回文串集合。
比如一个简单的例子,求将一个字符串划分为若干个回文串,有多少种划分方案。那么转移式子就应该是:
枚举 \(j\) 显然没啥前途,我们考虑对原串构造 PAM。那么当建到 \(i\) 这一位时,假如此时在 PAM 上对应的节点为 \(p\),那么转移式子其实就是:
这样就可以除掉 \(s[j, i] \in P\) 的限制了。但是这样改完之后,一直跳 fail 仍然是 \(O(n)\) 的,复杂度貌似没啥改变。
此时考虑我们上面得到的性质:那么从 \(p\) 到根节点的所有节点可以划分为 \(\log n\) 条链。我们不妨对这 \(\log n\) 条链维护答案。
具体的,设 \(top_u\) 为 \(u\) 节点所在链的链顶的父节点,\(diff_u\) 为 \(len_u-len_{fail[u]}\),这两条信息都可以在构建 PAM 的时候顺便维护出来。
然后考虑维护另一个 DP 数组 \(g_u\) 表示从 \(u\) 节点一直到 \(top_u\) 节点(不含 \(top_u\))的答案和。
由于 \(len\) 构成等差数列,\(g_u\) 是很好转移的。事实上,\(g_u\) 只比 \(g_{fail[u]}\) 多一个答案。可以举一个例子来看怎么转移。
假如现在转移到第 \(7\) 位,那么此时 PAM 上对应的节点的 \(g_u\) 应当为 \(f_0+f_2+f_4+f_6\) (即图中打 x 的地方)
假设现在转移到了第 \(9\) 位,那么发现实际上现在的 \(g_u\) 仅仅比 \(g_{fail[u]}\) 多出链中最短的串对应的 \(f_i\)(也就是红色的 x)。而当前链中最短的串的长度 \(l = len_{top_u} + diff_u\),那么当前要加上的答案就是 \(f_{i - l}\)。
也就是说,\(g_u\) 的转移式子为 \(g_u = g_{fail[u]} + f_{i - l}\)。需要注意 \(fail[u]\) 是否与 \(u\) 在同一个链内,如果不是就不能加上 \(g_{fail[u]}\)。
代码大概长这样:
void solve(int i) {
for (int p = lst; p > 1; p = top[p]) {
int l = len[top[p]] + diff[p];
g[p] = f[i - l];
if (diff[p] == diff[fail[p]]) {
g[p] += g[fail[p]];
}
f[i] += g[p];
}
}
于是每次插入一个字符之后执行一次 solve
函数即可维护出 \(f\) 数组。
放几道例题:
IITKWPCE - Let us play with strings
这个是纯板子,只不过从求和改成了找最小值。那么把上面的加和改为 \(\min\) 即可。
CF932G Palindrome Partition
题目要求将字符串划分偶数段,求满足 \(s_i=s_{k-i+1}\) (\(k\) 为划分的字符串数)的划分方案数。
我们可以将字符串复制一遍翻转过来,然后按照奇偶顺序将两个字符串插入到一起,那么上面的条件就转化成了求偶回文串的划分方案数的问题了。于是直接套上面的板子即可。
CF906E Reverses
题目给出 \(s,t\) 两个字符串,求至少需要翻转 \(s\) 中的多少不相交的区间才能够使 \(s=t\)。
同样发现假如按照奇偶将 \(s\) 与 \(t\) 合并,那么如果一个区间翻转之后与 \(t\) 中对应区间相等当且仅当新构造的字符串对应区间为偶回文串,于是直接求该字符串最少划分成多少个回文串。
注意到当长度为 \(1\) 时不需要翻转,所以我们在转移的时候直接判断一下,假如现在最后两位相同那么直接从 \(f_{i-2}\) 向 \(f_i\) 转移一下。注意 \(g_i\) 转移的时候不可以进行这样的特判。(具体看一下上面的图就明白了,转移的时候长度为 \(2\),但是到后面的时候长度就不是 \(2\)了,虽然本题转移了也不会错,但还是要注意转移中的式子不可以跟串的长度有关)
这题还需要输出方案,于是同时记录一下 \(f\) 和 \(g\) 数组的决策点即可。
HDU5340 Three Palindromes
可以整个二维 DP,然后用同样的方式转移即可。
URAL1954 Five Palindromes
怎么又有一个这种题