hihocoder 1032 最长回文子串 (Manacher算法 详解+模板)

时间限制:1000ms
单点时限:1000ms
内存限制:64MB

描述

   小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。

   这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?”

   小Ho奇怪的问道:“什么叫做最长回文子串呢?”

   小Hi回答道:“一个字符串中连续的一段就是这个字符串的子串,而回文串指的是12421这种从前往后读和从后往前读一模一样的字符串,所以最长回文子串的意思就是这个字符串中最长的身为回文串的子串啦~”

   小Ho道:“原来如此!那么我该怎么得到这些字符串呢?我又应该怎么告诉你我所计算出的最长回文子串呢?

   小Hi笑着说道:“这个很容易啦,你只需要写一个程序,先从标准输入读取一个整数N(N<=30),代表我给你的字符串的个数,然后接下来的就是我要给你的那N个字符串(字符串长度<=10^6)啦。而你要告诉我你的答案的话,只要将你计算出的最长回文子串的长度按照我给你的顺序依次输出到标准输出就可以了!你看这就是一个例子。”

提示一提示二提示三提示四
样例输入
3
abababa
aaaabaa
acacdas
样例输出
7
5
3 

题目链接:http://hihocoder.com/problemset/problem/1032


题目分析:Manacher算法可以在O(n)的时间复杂度内解决最长回文子串问题,下面介绍一下这个算法

首先对于一个任意长度的字符串,通过插入无关字符法均可以将其变成奇数长度,如aba => #a#b#a#,abba => #a#b#b#a#,为了解决边界问题可以直接在最前面再加上一个无关字符,令cur为当前能延伸到最右端的回文子串的中心位置,p[cur]表示当前能延伸到最右端的回文子串的回文半径,而p[cur] + cur就是当前能延伸到的最右端,当前位置i如果在其范围之外,即p[cur] + cur < i则p[i] = 1(自己另起一段回文子串),如果p[cur] + cur >= i,也就是当前位置在其范围内,则此时p[i] = min(p[cur * 2 - i],p[cur] + cur - i),这里分两种情况,1) p[cur * 2 - i] > p[cur] + cur - i,也就是说以i当前的对称点为中心的回文子串范围在当前cur为中心的回文子串的最左端的左边,则这时p[i] = p[cur] + cur - i;p[cur] + cur - i指的是当前cur为中心的回文串的最右端到当前点i的距离,2) p[cur * 2 - i] <= p[cur] + cur - i,情况类似上面,画图很容易看出来,算出p[i],则以当前的i为中心向两端扩展,若扩展出来的最右端超过原来的最右端则更新cur

#include 
#include 
#include 
using namespace std;
int const MAX = 1e6 + 5;
char s[MAX << 1];
int p[MAX << 1];

int Manacher()
{
	int len = strlen(s);
	for(int i = len; i >= 0; i--)
	{
		s[(i << 1) + 2] = s[i];
		s[(i << 1) + 1] = '#';
	}
	s[0] = '*';
	int cur = 0, ans = 0;
	for(int i = 2; i < 2 * len + 1; i++)
	{
		if(p[cur] + cur >= i)
			p[i] = min(p[(cur << 1) - i], p[cur] + cur - i);
		else
			p[i] = 1;
		while(s[i - p[i]] == s[i + p[i]])
			p[i] ++;
		if(p[cur] + cur < i + p[i])
			cur = i;
		ans = max(ans, p[i]);
	}
	return ans - 1;
}

int main()
{
	int n;
	scanf("%d", &n);
	while(n --)
	{
		scanf("%s", s);
		printf("%d\n", Manacher());
	}
}


你可能感兴趣的:(ACM,数据结构,字符串)