Manacher算法

Posted nymph

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Manacher算法相关的知识,希望对你有一定的参考价值。

问题

给出一个字符串,求出最长的回文串。

思路

朴素的想法:枚举字符串上的每一位,以其为回文串的中心进行扩展,统计答案。
这种方法是O(N^2)的,不优秀。

接下来考虑线性做法:
先将字符串中间插入特殊符号,以处理偶数长度的回文串。
对于每个回文串,我们可以给它记两个信息,即中心和半径r。再记录一个值mx代表当前扩展到的最右边界,以及这个回文串的中心p。
枚举每一个字符,分类讨论(以下j为i关于p的对称点):
(1)\\(mx\\leq i\\),r[i]\\(\\geq\\) 1(此时j无法给i提供信息)

(2)\\(mx-i>r[j]\\),r[i]=r[j](由于回文串的对称性可得)

(3)\\(mx-i\\leq r[j]\\),r[i]\\(\\geq\\)mx-i(由于[mx\',mx]外的对称性无法满足,j给i提供了一个下界)

在初始的r[i]上再进行扩展即可,并更新mx和p。
由于扩展成功会导致mx右移,而mx右移不超过n次,故左右扩展总复杂度是O(N),枚举i也是O(N),故总时间复杂度为O(N)。

例题:

双倍回文

题意

记字符串x的倒置为\\(x^R\\),如果字符串s可以写成\\(ww^rww^r\\),则称它是一个双倍回文。
现在给出一个字符串,求出最长的双倍回文子串。

思路

在manacher中,考虑以当前枚举到的i为中心求双倍回文串。
当r[i]可以从r[j]推过来(情况2、3),我们就不需要判断这一部分的回文串,因为它与j处的回文串相同。
由此可知,一个字符串中本质不同的回文子串数量级是O(N)的
于是在每次mx扩展成功时再枚举。因为mx最多右移n次,所以这个复杂度也是O(N)的。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>

const int N = 5e5 + 1;
int n, ans;
int hw[N << 1];
char a[N], s[N << 1];

int main() {
	scanf("%d", &n);
    scanf("%s", a);
    s[0] = \'@\';
	s[1] = \'#\';
    for (int i = 0; i < n; i++) {
        s[i * 2 + 2] = a[i];
        s[i * 2 + 3] = \'#\';
    }
    n = n * 2 + 2;
    s[n] = 0;
    int mx = 0, mid;
    for (int i = 1; i < n; i++) {
        if (i < mx)
            hw[i] = std::min(hw[mid * 2 - i], mx - i);
        else
			hw[i] = 1;
		while (s[i + hw[i]] == s[i - hw[i]])
			hw[i]++;
        if (hw[i] + i > mx) {
        	if (i & 1)
        		for (int j = std::max(i + 4, mx); j <= i + hw[i]; j++) {
        			if (!(j - i & 3) && hw[i - (j - i) / 2] >= (j - i) / 2)
        				ans = std::max(ans, j - i);
				}
            mx = hw[i] + i;
            mid = i;
        }
    }
    printf("%d", ans);
}

以上是关于Manacher算法的主要内容,如果未能解决你的问题,请参考以下文章

Manacher 入门+模板 回文串专用算法

最长回文子串---Manacher算法

什么是Manacher(马拉车)算法-java代码实现

P3805 模板manacher算法(马拉车)

[BZOJ3676][APIO2014]回文串(Manacher+SAM)

Manacher算法详解