例题 HDU-5421 翻译

如果字符串是静态的,则可以使用 Manacher 算法。但本题的字符串可以动态在首位添加字符,无法使用 Manacher。

本题可以使用回文树(回文自动机)算法。该算法的时间复杂度为 \(\mathcal{O}(N)\),但空间复杂度较差,因为其本质上是字典树。

1 回文树的关键技术

回文树的关键技术为奇偶字典树 \(+\) 后缀链跳跃。下面给出思维过程。

  1. 后缀链跳跃

比如在 abcb 后面插入 a,则新插入的 a 与第一个 a 之间的所有字符构成了一个回文串。

那如果是插入了 c,其不能与开头的 a 构成回文串,该怎么办呢?

可以发现上面插入 a 时,本质上就是尝试在原串的后缀回文串 bcb 左右各扩展一个字符。如果新插入的字符与上一个回文串的前一个字母相同,那这个回文串就可以向左右各扩展一个字符了。但如果不同,那我们就应该找下一个短一点的回文串进行尝试,如上面的例子中尝试扩展 bcb 失败,接下来以最后一个 b 结尾的最长回文串为 b 本身,我们再尝试扩展它,扩展成功。b 扩展成为 bcb

我们称这个不断找最长后缀回文串的过程为后缀链跳跃

  1. 奇偶字典树

我们可以用两棵字典树来存储回文串,一棵存长度为奇数的,一棵存长度为偶数的。我们可以用结点 \(0,1\) 分别表示偶数字典树和奇数字典树的根。在存储回文串时,我们只存一半。比如回文串 abccba,我们在偶数字典树上加入 \(0 \to\) c \(\to\) b \(\to\) a,这样我们从下往上读,读到 \(0\) 后再读下来就是原串。如果是 abcba,那我们在奇数字典树上加入 \(0 \to\) c \(\to\) b \(\to\) a,这样我们从下往上读再读回来,但最上面的边只读一次。

下图是 zaacaac 的存储方式:

  1. 建回文树