BZOJ2565
2565: 最长双回文串
Description
顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。
输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。
Input
一行由小写英文字母组成的字符串S。
Output
一行一个整数,表示最长双回文子串的长度。
Sample Input
baacaabbacabb
Sample Output
12
HINT
样例说明
从第二个字符开始的字符串aacaabbacabb可分为aacaa与bbacabb两部分,且两者都是回文串。
对于100%的数据,2≤|S|≤10^5
2015.4.25新加数据一组
Source
[2012国家集训队Round 1 day2](http://www.lydsy.com/JudgeOnline/problemset.php?search=2012国家集训队Round 1 day2)
解题思路:
这道题目刚刚看到的时候一点思路都没有,后来看了题解才知道,对于S串和T串,我们可以知道,他们是不相交,但却相连的。我们一看到求最长回文字串,不难想到Manacher算法,回忆Manacher算法,且搞两个DP数组,保存从左往右以字符i结尾时回文串的长度,和从右往左以字符i结尾时回文串的长度,我们知道他需要在每个间隙插入一个特殊字符,所以我们可以枚举S和T中间的那个特殊字符,将两个DP数组的值相加,取个max就好了。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 210000 + 5;
int RL[maxn],dp1[maxn],dp2[maxn],ans;
char sc[maxn],s[maxn];
int main(){
scanf("%s",sc);
memset(s,'#',sizeof(s));
memset(RL,0,sizeof(RL));
int len = strlen(sc);
for(int i = 0,j = 1;i < len; i++,j += 2)s[j] = sc[i];
len = (len<<1) + 1;
int pos = 0,max_right = 0;
for(int i = 0;i < len; i++){
if(i < max_right)RL[i] = min(RL[2*pos-i],max_right-i);
else RL[i] = 1;
while(s[i - RL[i]] == s[i + RL[i]])RL[i]++;
if(i + RL[i] > max_right)max_right = i + RL[i],pos = i;
}
int last = 0;
for(int i = 0;i <= len; i++){
if(i + RL[i] > last){
for(int j = last+1;j <= i + RL[i]; j++)
if(s[j] != '#')dp1[j] = j-i+1;
last = i + RL[i]-1;
}
}
last = len-1;
for(int i = len-1;i >= 0; i--){
if(i-RL[i] < last){
for(int j = last-1;j >= i - RL[i]; j--)
if(s[j] != '#')dp2[j] = i-j+1;
last = i - RL[i]+1;
}
}
for(int i = 1;i <= len-1; i++)
if(s[i] != '#')ans = max(ans,dp1[i] + dp2[i+2]);
printf("%d\n",ans);
return 0;
}