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);
}
posted @ 2017-04-11 07:32  Zeonfai  阅读(121)  评论(0编辑  收藏  举报