hiho一下第一周

题目1 : 最长回文子串

时间限制: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

思路

题目要求最长回文串,回文串的定义在题目中已经给出,对于求最长回文串的最直接的方法就是暴力枚举,也就是提示一当中的方法,显然这个方法是会超时的,为了验证我的想法,我还交了一发。
具体的枚举函数如下:

int Solve1(){
	int len = strlen(str);//str便是输入的字符串
	for (int i = len; i >= 1; i--){
		for (int j = 0; j + i - 1 < len; j++){
			int a, b;
			for (a = j, b = j + i - 1; a <= b; a++, b--){
				if (str[a] != str[b])
					break;
			}
			if (a > b)
				return i;返回值i就是最长回文串的长度
		}
	}
	return 0;
}

根据提示二,我试了枚举中心点的方法,因为有aba、abba两类情况,所以我枚举了两遍,具体代码如下:

int Solve2(){
	int max = 0;
	int len = strlen(str);
	for (int i = 0; i < len; i++){
		int j;
		for (j = 1; (i - j) >= 0 && (i + j) < len&&str[i - j] == str[i + j]; j++);
		max = Max(max, 2 * j - 1);
		//printf("i=%d,j=%d,max1=%d\n", i, j, max);
	}
	for (int i = 1; i < len; i++){
		int j;
		for (j = 0; (i - j - 1) >= 0 && (i + j) < len&&str[i - j - 1] == str[i + j]; j++);
		max = Max(max, 2 * j);
		//printf("i=%d,j=%d,max2=%d\n", i, j, max);
	}
	return max;
}

不出意外,上面这个算法超时了,因为会有aaaaaa这种字符串,会有重复枚举的情况发生,对此我们来看提示三和提示四。

根据提示三和提示四可知,这是个神奇的算法----manacher算法
没听过manacher的孩子可以看下面这个博客:
http://blog.csdn.net/xingyeyongheng/article/details/9310555
这个博客讲的挺详细的,一般都能看懂。

下面的代码是我套用了上面那个博客的代码(本来自己写了个,发现没他的好):

int Solve3(){
	int len = strlen(str);
	clr_all(p, 0);
	for (int i = len; i >= 0; i--){
		str[i + i + 2] = str[i];
		str[i + i + 1] = '#';
	}
	str[0] = '*';
	int id = 0;
	int maxlen = 0;
	for (int i = 2; i < 2 * len + 1; i++){
		if (p[id] + id>i)
			p[i] = Min(p[2 * id - i], p[id] + id - i);
		else
			p[i] = 1;
		while (str[i + p[i]] == str[i - p[i]])
			p[i]++;
		if (i + p[i] > id + p[id])
			id = i;
		if (maxlen < p[i])
			maxlen = p[i];
	}
	return maxlen - 1;
}

然后就AC了

总代码:

#include
#include
#include
#include
#include
using namespace std;
#define clr_all(a,b) memset(a,b,sizeof(a))
#define clr(a,b,n) memset(a,b,sizeof(a[0])*n)
template<typename T> T Max(T a, T b){ return a > b ? a : b; }
template<typename T> T Min(T a, T b){ return a < b ? a : b; }
const int MAXN = 1000000 + 10;
char str[MAXN*2];
int p[MAXN * 2];
int Solve1(){
	int len = strlen(str);
	for (int i = len; i >= 1; i--){
		for (int j = 0; j + i - 1 < len; j++){
			int a, b;
			for (a = j, b = j + i - 1; a <= b; a++, b--){
				if (str[a] != str[b])
					break;
			}
			if (a > b)
				return i;
		}
	}
}
int Solve2(){
	int max = 0;
	int len = strlen(str);
	for (int i = 0; i < len; i++){
		int j;
		for (j = 1; (i - j) >= 0 && (i + j) < len&&str[i - j] == str[i + j]; j++);
		max = Max(max, 2 * j - 1);
		//printf("i=%d,j=%d,max1=%d\n", i, j, max);
	}
	for (int i = 1; i < len; i++){
		int j;
		for (j = 0; (i - j - 1) >= 0 && (i + j) < len&&str[i - j - 1] == str[i + j]; j++);
		max = Max(max, 2 * j);
		//printf("i=%d,j=%d,max2=%d\n", i, j, max);
	}
	return max;
}
int Solve3(){
	int len = strlen(str);
	clr_all(p, 0);
	for (int i = len; i >= 0; i--){
		str[i + i + 2] = str[i];
		str[i + i + 1] = '#';
	}
	str[0] = '*';
	int id = 0;
	int maxlen = 0;
	for (int i = 2; i < 2 * len + 1; i++){
		if (p[id] + id>i)
			p[i] = Min(p[2 * id - i], p[id] + id - i);
		else
			p[i] = 1;
		while (str[i + p[i]] == str[i - p[i]])
			p[i]++;
		if (i + p[i] > id + p[id])
			id = i;
		if (maxlen < p[i])
			maxlen = p[i];
	}
	return maxlen - 1;
}
int main(){
	int T;
	int cas;
	for (scanf("%d", &T), cas = 1; cas <= T; cas++){
		scanf("%s", str);
		printf("%d\n", Solve3());
	}
	return 0;
}

你可能感兴趣的:(ACM-ICPC)