题目1:最长回文子串
题目原文:http://hihocoder.com/contest/hiho1/problem/1
【题目解读】
题目与 POJ 3974 palindrome 基本相同。求解最长回文子串:manacher算法
理解了很长时间,才理解了大概。然后在编码过程中,又不断有细节错误,写了两天才写完。
manacher 算法的具体过程见以上网址。其中的精髓有:
(1)利用回文串的对称性,首先对于以某个位置为中心的回文串长度,“预测”一个下限,减少之后的计算。
(2)不断扩大“预测”的范围,因此需要最远的右边界,即最大的 max = id + p[id]。
【hiho提示】
【提示一】【提示二】都是暴力求解,不过提供了极好的判断最长回文子串的方法“不是枚举它的起止位置而是尝试枚举子串的中心位置,然后再从小到大依次枚举这个子串的长度,一旦发现已经不是一个回文串了就继续尝试下一个中心位置。”
【提示三】介绍了manacher算法。
【提示四】提出了关于字符串中心点导致的奇偶性问题,同时提出了“在原来字符串的基础上,在任意两个相邻的字符间都插入一个特殊字符,是不是无论是原来字符串中长度为奇数的回文子串还是长度为偶数的回文子串,在新的字符串中都有一个长度为奇数的回文子串与之进行对应呢?”
其实还可以考虑使用另外一个特殊字符作为字符串的开头标志。
【编写细节】
以下是我自己在编写中碰到的各类逗比问题。。。总结自己,以示后人。
(1)原本尝试使用 str 来扩展原本的字符串变为插入特殊字符的字符串。但是此举改变了字符串原先的长度 strlen,并且造成了很多其他问题。。。于是新开了数组。
(2)10^6 的数组完全可以开,建议用 char* 不要用 string(其实我 string 不熟悉)。
(3)因为需要扩大可“预测”范围,所以最右终点 max 不断右移,与之对应的中心 id (j 的对称点是2*id-j )也不断变化。但是这俩与最大子串长度 max_p 没有联系,需要分为两组比较并存储。
(4)最开始 while() 计算回文串长度时我写了一个函数,但是我函数最开始置 0 了回文串半径。。。SB啊!那我之前的“预测”不就白做了么!逗比啊!
(5)最神奇的是。。。公用变量数组,如果放在main函数里就是RE,放在main函数外当作全局变量就是AC,好奇怪。。。希望高人解答!
跟心凯大神叫交流后,他的回答是,就是:main函数里面定义的都是局部变量,这些变量是在程序运行时分配到程序栈空间里面的。而定义在函数外的全局变量是定义在程序数据段(较大)的,RE是因为栈的大小不够分配那么多的空间,多出来的空间超出了栈的范围,不能被Main函数访问,就报错了。
所以在C里面开大数组都是开成全局变量。
【AC代码】
#include<stdio.h> #include<stdlib.h> #include<string.h> #define MAX 1000005 char str[MAX]; char s[MAX*2]; long p[MAX*2]; long len, slen; long mx, id, mxp; int t; int main() { scanf("%d", &t); for(int k=0; k<t; k++) { scanf("%s", str); len = strlen(str); s[0] = '#'; for(long i=0; i<len; i++) { s[i*2 + 1] = str[i]; s[i*2 + 2] = '#'; } slen = len*2+1; id = 0; mx = 0; mxp = 0; memset(p, 0, MAX*sizeof(long)); for(long j=0; j<slen; j++) { if(mx > j) { p[j] = p[2*id-j]>(mx-j)?(mx-j):p[2*id-j]; } while(s[j-p[j]]==s[j+p[j]] && (j-p[j]>=0) && (j+p[j]<slen)) p[j]++; if(mxp < p[j]) mxp = p[j]; if(mx < (j+p[j])) { id = j; mx = (j+p[j]); } } printf("%ld\n", mxp-1); } return 0; }