manacher算法

  • 2022 年 1 月 30 日
  • 筆記

前置芝士:扩展KMP(本人认为扩展KMP反而和manacher更像

0. 约定

字符串的下标从 \(0\) 开始。\(|s|\) 表示字符串 \(s\) 的长度。
对于字符串 \(s\),记其每一个字符分别为 \(s_0, s_1, \cdots, s_{|s|-1}\)
子串 \(s_l, s_{l+1}, \cdots, s_{r-1}, s_r\) 简记为 \(s[l:r]\)。特别地,若 \(l=0\),可记作 \(s[:r]\);若 \(r=|s|-1\),可记作 \(s[l:]\)
对于字符串 \(a, b\)\(a+b\) 表示拼接操作,即将字符串 \(b\) 拼接到字符串 \(a\) 之后,构成新的字符串。
记构成的新字符串为 \(c\),则上述拼接操作记为 \(c\gets a+b\)
其中符号 \(x\gets y\) 表示将 \(y\) 的值赋给 \(x\)
不论是字符还是字符串,皆不加引号。

1. 问题

目标:快速求出一个字符串的所有回文子串的长度。

首先我们进行一些预处理,是过程简单化:将原字符串的字符间插入原字符串中不存在的字符。
例:
\(\texttt{ababa}\rightarrow\texttt{~#a#b#a#b#a#}\)
\(\texttt{abaaba}\rightarrow\texttt{~#a#b#a#a#b#a#}\)
(为了方便判断越界在最前面加了另外一个字符)
可以看出,这样操作的好处是让奇回文串和偶回文串统一为了奇回文串。
奇回文串的好处是有一个中心字符,从它向两边扩展,回文串内对应的字符也相同。
这就引出了中心扩展算法。

中心扩展算法的思想非常简单,就是枚举中心字符,每次向两边能扩展就扩展,这样就求出了所有的回文子串。
但是,对于 \(\texttt{aaaaaaaaa}\) 这样的字符串,中心扩展算法的时间复杂度达到了 \(O(n^2)\),显然是不可以接受的。
因此,我们需要利用回文串的性质,对上述算法进行改进。

2. 性质