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;
}
posted @ 2017-09-04 19:59  Frade~  阅读(221)  评论(0编辑  收藏  举报