专栏文章
Manacher 入门
算法·理论参与者 9已保存评论 9
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 9 条
- 当前快照
- 1 份
- 快照标识符
- @miqpdazi
- 此快照首次捕获于
- 2025/12/04 08:33 3 个月前
- 此快照最后确认于
- 2025/12/04 08:33 3 个月前
前言
描述
给定一个长度为 的字符串 ,求字符串 的最长回文子串长度。
1. 暴力
可以使用中心扩展法,对于回文串,枚举中心点,在往两端扩展。要分类讨论奇偶性,总复杂度 。
给出部分代码。
长度为奇数
CPPint k = 0;
while(s[i - k] == s[i + k]) k++;
长度为偶数
CPPint l = 0, r = 1;
while(s[i - l] == s[i + r]) l++, r++;
2. 优化
Part1
可以发现,每次检查回文都会重复计算,于是可以参考 KMP 的思路,跳过已检查过的点。
Part2
在进行中心扩展时,要分类讨论字符串长度的奇偶性。那在 Manacher 中如何避免呢?在字符串 中插入一种 中没有的字符,可以用
#。举个例子,把 "abcab" 操作后变成 "#a#b#c#a#b#"。为了防止越界,要在两端加上两个不同的奇怪字符。不同是为了防止检查回文时多匹配。
这种方法是如何避免奇偶性讨论的,可以自行思考。
3. 正式优化
定义数组 , 为以 为中心点的最长回文串的半径。
Manacher 的核心思想是“回文的镜像也是回文”。如图,已经求出了 。则它左边的阴影部分是以 为中心的回文,由于回文是对称的,所以右边的镜像部分 与 回文 相同,那么在计算以 为中心时,可以直接从长度 开始匹配。然而这只是一种情况,下面是 Manacher 的完整思路。假设已经求出了 ,现在求 。定义 为 这些回文串中最大的中心点, 为 对应的回文串的右端点。那么 就等于 ,且 是已检查字符串的右端点。通俗的说, 左边的都检查过,右边的都没检查。
现在有两种情况。
因为 右边的都没检查,所以只能将 设为 然后进行暴力。
这种情况,又可以细分为两种小情况。
- 上文叙述了这种情况,这里仅做补充。如何得知 的位置?因为 ,所以 。然后对 进行中心扩展即可。
2.当 的回文在 对应字符串的外部时,那么由图可知, 可确定的回文也只有 到 这段了,当然这段长度是 ,所以以这段为基础进行中心扩展即可。

4. 答案
举个例子就明白了:
#a#b#b#a# #a#b#c#b#a#观察可以得到,答案即为半径减一。
5.模板
可以先消化完上面的内容再来做。因为上文讲的很清楚了,这里只给出代码。
CPPvoid manacher(char *t)
{
int r = 0, c = 0;
for(int i = 0;i < n;i++)
{
if(i < r) p[i] = min(p[(c << 1) - i], r - i);
else p[i] = 1;
while(t[i - p[i]] == t[i + p[i]]) p[i]++;
if(i + p[i] > r)
{
r = i + p[i];
c = i;
}
}
}
6.应用
题意很好理解,看到最长回文子串显然可以想到 Manacher。可以观察到双回文子串就是两个回文子串,于是把双回文子串分成两部分。统计以这位为结尾或开头的最长回文子串。这部分可以在中心扩展时维护。答案即为两者相加。
参考代码
CPP#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, ans, p[N << 1], l[N << 1], r[N << 1];
string s, t;
void manacher(string t)
{
int n = t.size();
int R = 0, c = 0;
for(int i = 0;i < n;i++)
{
if(i < R) p[i] = min(p[(c << 1) - i], R - i);
else p[i] = 1;
l[i - p[i] + 1] = max(l[i - p[i] + 1], p[i] - 1);
r[i + p[i] - 1] = max(r[i + p[i] - 1], p[i] - 1);
while(t[i - p[i]] == t[i + p[i]])
{
p[i]++;
l[i - p[i] + 1] = max(l[i - p[i] + 1], p[i] - 1);
r[i + p[i] - 1] = max(r[i + p[i] - 1], p[i] - 1);
}
if(i + p[i] > R)
{
R = i + p[i];
c = i;
}
}
}
int main()
{
cin >> s;
n = s.size();
t = "$#";
for(int i = 0;i < n;i++)
{
t.push_back(s[i]);
t.push_back('#');
}
t.push_back('&');
manacher(t);
n = t.size();
for(int i = 3;i < n - 2;i++)
if(t[i] == '#')
ans = max(ans, l[i] + r[i]);
cout << ans;
return 0;
}
7.习题
相关推荐
评论
共 9 条评论,欢迎与作者交流。
正在加载评论...