manacher马拉车算法学习笔记
马拉车是用于线性求回文子串的算法,适用于很多关于回文有个的题目,包括但不限于:最长回文子串,回文串计数,等等
马拉车其实可以看做中心拓展法的优化,也就是以某个字符为中心,同时向左右找相同的字符。但是由于这种判断不了偶数的回文,我们就要在中间加一些字符:
比如字符串“abcba”,我们就可以变成“#a#b#c#b#a#”,这里开头和结尾都加入的特殊字符,方便计算。另外这里其实'#'可以换成任意的字符比如说'a'
然后我们这里有两个关键的变量,r和c,分别表示目前最长的回文串最右边的下一个字符,和这个回文串的中心(如果有多个就默认是最前面的那个),为什么要有这两个变量呢?因为我们可以根据回文串的性质,来快速计算当前的最长回文子串半径,我们用p数组表示
然后分类讨论:
1.如果i>=r,那前面的回文串不就没用了吗,直接中心拓展
如果i<r,那么我们可以找i关于c的对称点,记作i`,可以计算得到i`=2*c-i,这里还需要继续分类讨论,这里我给个图理解吧,()表示以c为中心的大回文串,[]表示以i`为中心的回文串
2.(...[.i`.]...c...i...),[]被()完全包住了,因为对称,所以直接p[i] = p[i`],也就是p[i] = p[2*c-i],不可能更多了,i如果还能拓展的话这说明i`也要能拓展,但p[i`]已经是最大的了,无法拓展,矛盾。
3.[.(.i`..]...c...i...),[]超出了(),那么p[i] = r-i,说白了就是r边界内的的串一定是合法的,这个可以看情况2理解,但为什么不可能更多呢,因为如果更多,那么这就说明c还能再扩展,但p[c]已经是最大的了,矛盾
4.如果他刚好卡在线上:就是'['和'('重合了,还是根据2,他在r以内的一定是合法的,但是他可以更多!因为他没有回文的限制了,i继续扩展跟i`和c没有半毛钱的约束关系了。
前面的你看不懂,觉得很晕,正常,不过其实会写马拉车根本用不到这么复杂的东西。
我们根据前面的推导,有一个点是一种反复在说的,就是r边界内的一定合法,对于i<r,那么它是不是至少有min(p[2*c-i],r-i)的长度,对于i>=r,他自身是不是就是个回文串,最少长度直接就是1。
这时,我们有了最小长度,直接不需要分类讨论,无脑开始中心拓展。因为如果不能继续扩的情况,他一次都不需要跑,时间复杂度不变
最后更新一下c和r就行了,注意我们所有的操作都是基于变化后的字符串的,原始字符串的回文串长度=变化后回文串半径-1
code:
int p[N];
string ss;
void manacher() {
for (int i = 0, c = 0, r = 0;i < ss.size();i++) {
int len = i < r ? min(p[2 * c - i], r - i) : 1;
while (i - len >= 0 && i + len < ss.size() && ss[i - len] == ss[i + len])len++;
if (i + len > r) {
c = i;
r = i + len;
}
p[i] = len;
//maxx = max(maxx,len-1)
}
}

浙公网安备 33010602011771号