Manacher 马拉车算法
一、概述与定义
-
马拉车算法是一种用来解决最长回文串等关于回文串长度问题的算法。
-
定义一个长度为奇数 \(x\) 回文串 \(s\) 的回文中心为 \(\dfrac{x+1}{2}\)。
-
在此定义下,一个有回文中心的回文串必然是一个奇数。
-
定义一个长度为 \(n\) 的回文串 \(s\) 的回文长度为 \(\lceil \dfrac{x}{2} \rceil\)。
二、做法
-
以字符串 \(s=\mathtt{dcbababcd}\) 为例
-
我们首先在字符串 \(s\) 的字符之间插入一个特殊字符 \(\mathtt{\#}\),变成:
\[s=\mathtt{\# d \# c \# b \# a \# b \# a \# b \# c \# d \#} \] -
这么做的目的就是消除奇数长度字符串和偶数长度字符串的长度差别。可以观察到,一个以 \(\mathtt{\#}\) 为中心的回文串去掉 \(\mathtt{\#}\) 后就是一个偶数长度字符串,一个以字母为中心的字符串就是一个奇数长度字符串。不妨定义 \(p_i\) 为以 \(i\) 为回文中心的最长回文串的回文长度,那么 \(p_i-1\) 就是一个回文串的长度(具体是哪个回文串写出来会比较抽象,这里简略)。
-
我们再在首尾两端插入一个特殊字符串,以防止遍历的时候越界。定义刚刚没有插的时候的那一段字符串为有效段。
-
不妨假设 \(1\) 到 \(i-1\) 这一段中,末尾位置最后的回文串的末尾位置为 \(mx\),它是一个以 \(c\) 为中心的回文串,
-
对于 \(i > mx\) 的情况,由于前面关于字符串的信息最多到 \(mx\),那么你木得任何办法,只能设置 \(p_i=1\),然后首尾扩展。
-
对于 \(i \le mx\) 的情况,不妨假设 \(i\) 关于 \(c\) 的对称点为 \(j=2c-i\),注意到,一个以 \(i\) 为回文中心的回文串经过 \(c\) 对称后必然也会存在一个以其对称点 \(j\) 为回文中心的回文串,原因就是以 \(i\) 为回文中心的回文串对称后反了过来,变成了倒序,但依然是一个回文串。
-
显然,如果 \(p_j\) 超过了 \(mx-i\),说明以 \(j\) 为中心的字符串长度超过了 \(c\) 的“对称管辖范围”,是否对称还说不定。那么我们的信息最多也就到 \(mx-i\) 这里,剩下的只能靠暴力拓展了。而如果没超过,那半点问题都没有。
-
所以我们实现时可以这样
- 如果 \(i>mx\),那么 \(p_i = 1\),否则 \(p_i=\max(p_{2c-i},mx-i)\)。
- 暴力拓展:当 \(s_{i-p_i} = s_{i+p_i}\) 时,\(p_i\) 加上 \(1\)。
- 更新 \(mx\),如果 \(i+p_i\) 大于 \(mx\),那么 \(mx=i+p_i\),\(c=i\)。