Manacher算法学习笔记
@(学习笔记)[Manacher]
问题概述
给定一个串\(str\), 要求出其以每一位为对称中心的最长回文串的长度
解决方法
思路: 充分利用已有信息, 做到\(O(n)\)复杂度.
首先明确一下字符串\(str\)的一个子串的表示方式: \(str[i ... j] (i \le j)\)表示原串从第\(i\)位到第\(j\)位形成的子串; \(str[j ... i] (i \le j)\)反之.
我们发现回文串有两种, 一种长度为奇数(如\(ababa\)), 另一种长度为偶数(如\(abba\)). 它们的对称中心是不一样的, 分别是一个字母和两个字母见的空隙. 为了避免分类讨论, 我们在每两个字母之间插入一个分隔符, 这样就只要考虑长度为奇数的回文串了. 我们令p[i]
表示以第\(i\)位为对称中心的回文串向两边延伸的最大长度(包含对称中心, 例如串\(a|b|b|a\)中\(p[4] = 4\)). 假设我们已经处理出\(p[0 ... i - 1]\)的值, 我们记录下\(p[j \in [0, i - 1]] + j\)的最大值mx
, 和以\(mx\)为结尾的回文串的对称中心id
. 对于当前一位\(i\), 不难发现, \(str[i ... mx]\)与\(str[id \cdot 2 - i ... id - p[id]]\)在位置上关于\(id\)对称, 因此它们不超出\([id - p[id], id + [id]]\)的部分是相等的. 我们只需要在已有的这一部分的基础上继续往外扩张匹配, 然后再更新\(mx\)和\(id\)即可.
#include <cstdio>
#include <cctype>
#include <algorithm>
const int LEN = 1 << 20;
namespace Zeonfai
{
inline char getChar()
{
char c;
for(c = getchar(); ~ c; c = getchar());
return c;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("manacher.in", "r", stdin);
freopen("manacher.out", "w", stdout);
#endif
using namespace Zeonfai;
static char str[LEN << 1];
int len = 0;
str[len ++] = '<', str[len ++] = '#';
char c;
while(~ (c = getchar()))
str[len ++] = c, str[len ++] = '#';
str[len ++] = '>';
int mx = 0, id = 0, ans = 0;
static int p[LEN << 1];
for(int i = 0; i < len; ++ i)
{
p[i] = mx > i ? std::min(mx - i, p[(id << 1) - i]) : 1;
for(; str[i - p[i]] == str[i + p[i]]; ++ p[i]);
if(p[i] + i > mx)
mx = p[i] + i, id = i;
ans = std::max(ans, p[i]);
}
printf("%d", ans - 1);
}