专栏文章

Manacher

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mionkimo
此快照首次捕获于
2025/12/02 22:07
3 个月前
此快照最后确认于
2025/12/02 22:07
3 个月前
查看原文

定义

Manacher,它可以在 O(n)O(n) 的时间复杂度下,求出一个串的最长回文子串长度或所有回文串个数。

实现步骤

初始化

选择一个中心,向两边扩展,但如果字符串长度为偶数,就不好处理了。
此时,我们可以将字符串里两两字符中间都插入一个同样的字符,字符串开头的左边与结尾的右边也插一个与上面同样的字符,他就可以变成奇数长度的字符串,长度为 2×(s1)+12 \times (|s| - 1) + 1
CPP
void add() {
	str += '^';
	int len = s.size() - 1;
	for (int i = 0; i <= len; i++) {
		str += '#';
		str += s[i];
	}
	str += "#@";
	m = 2 * len + 1;
	return;
}

核心部分

定义 pip_i 为当中心为 ii 时的最长回文半径,RR 为最右回文边界 + 1,midmid 为 $R 代表的字符串的回文中心。
定义字符串 s = "ababa",则初始化后新字符串 str = "#a#b#a#b#a#",则 pp 为:
下标1234567891011
pip_i12141614121
注意,初始时,pip_i11,因为自己也算回文子串。
接着我们发现,pp 貌似也形成了一个回文数组。所以,我们可以得出以下结论:
  • i<Ri < R 时,我们在访问 ii 时,我们可以直接取 p2×midip_{2 \times mid - i} 的答案,但是,如果 i+pi>Ri + p_i > R,答案只能取 RiR - i。所以,结果应该是 min(p2×midi,Ri)\min(p_{2 \times mid - i}, R - i)
  • iRi \ge R 时,pip_i 只能等于 11
p[i] = ((i < R) ? min(p[2 * mid - i], R - i) : 1);

接着,pip_i 要向外扩散,以获取最优答案。
stri+pi=stripistr_{i + p_i} = str_{i-p_i} 时,pip_i 可以加一(向外扩散一步)。
CPP
while (str[i + p[i]] == str[i - p[i]]) {
    p[i]++;
}

最后,当 ii 变得比 RR 更优(更大)时,我们需要更新 RRmidmid
CPP
if (i + p[i] > R) {
    R = i + p[i], mid = i;
}

若题目要求求最长回文子串长度,因为我们之前加了字符,所以其实最长回文子串长度就是 pi1p_i - 1
ans = max(ans, p[i] - 1);

P3805 【模板】manacher

C
#include <bits/stdc++.h>

using namespace std;

const int N = 3e7 + 10;

string s, str;
int n, m, p[N];

void add() {
	str += '^';
	int len = s.size() - 1;
	for (int i = 0; i <= len; i++) {
		str += '#';
		str += s[i];
	}
	str += "#@";
	m = 2 * len + 1;
	return;
}

void manacher() {
	int R = 0, mid = 0, ans = 0;
	for (int i = 1; i <= m; i++) {
		p[i] = ((i < R) ? min(p[2 * mid - i], R - i) : 1);
		while (str[i + p[i]] == str[i - p[i]]) {
			p[i]++;
		}
		if (i + p[i] > R) {
			R = i + p[i], mid = i;
		}
		ans = max(ans, p[i] - 1);
	}
	cout << ans;
} 

signed main() {
	cin >> s;
	add(), manacher();
	return 0;
} 

P3501 [POI 2010] ANT-Antisymmetry

向外扩展的条件是:一个 00 一个 11。我们可以直接异或,特判双为字符情况即可。
CPP
#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 3e6 + 10;

string s, str;
int n, m, p[N];

void add() {
	str += '^';
	for (int i = 0; i < n; i++) {
		str += '#';
		str += s[i];
	}
	str += "#@";
	m = 2 * n + 1;
	return;
}

bool check(char x, char y) {
	return (((x - '0') ^ (y - '0')) == 1) || (x == '#' && y == '#');
}

void manacher() {
	int R = 0, mid = 0, ans = 0;
	for (int i = 1; i <= m; i += 2) {
		p[i] = ((i < R) ? min(p[2 * mid - i], R - i) : 1);
		while (check(str[i - p[i]], str[i + p[i]])) {
			p[i]++;
		}
		if (i + p[i] > R) {
			R = i + p[i], mid = i;
		}
		ans += (p[i] - 1) / 2;
	}
	cout << ans;
	return;
} 

signed main() {
	cin >> n >> s;
	add(), manacher();
	return 0;
} 

评论

0 条评论,欢迎与作者交流。

正在加载评论...