题目大意:
定义双回文串G是指一个可以被拆分成两个部分(S和T)的字符串G = S + T, 且S和T都是回文串的串, G自己本身可以不是回文串
给出一个长度为n ( 2 <= n <= 10^5 )的字符串, 求其最长双回文子串的长度
大致思路:
首先可以相当manacher算法当中有这样一个过程, 用两个辅助变量mx和p分别表示已有回文半径覆盖到的最右边界和对应中心的位置, 然后在求解过程中更新mx和p, 考虑到S和T的中间字符作为分隔的位置, 当这个位置被mx覆盖之后, mx从左往右更新第一次覆盖的时候p(覆盖它的串的中心)的位置一定是离这个位置最远的也就是找到了S的中心, 由于mx移动距离至多为n, 所以随着manacher算法的进行用dp来记录mx被更新时的那个回文串的信息即可, 找到S中心之后, 类似的反向从右往左做一遍manacher然后找到T的中心
这样O(n)的预处理之后找出了每个分隔符之下S和T的中心的最佳位置, 最后枚举一下分隔符的位置即可找到最优解
总体时间复杂度O(n)
manacher第一次一发AC, 果然还是要想清楚了敲更稳妥一些
代码如下:
Result : Accepted Memory : 4008 KB Time : 88 ms
/**************************************************************
Problem: 2565
User: Gatevin
Language: C++
Result: Accepted
Time:88 ms
Memory:4008 kb
****************************************************************/
/*
* Author: Gatevin
* Created Time: 2015/3/20 21:27:49
* File Name: Chitoge_Kirisaki.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define maxn 200020
char in[maxn], s[maxn];
int R[maxn];
int dpl[maxn], dpr[maxn];
/*
* 用dpl[i]表示第i位的位置作为两个回文串的分割字符时候
* 前面的回文串的中心在dpl[i]位置时最长
* 用dpr[i]表示第i位的位置作为两个回文串的分割字符的时候
* 后面的回文串的中心在dp[r]位置时最长
*/
void Manacher(char *s, int *R, int n)
{
int p = 0, mx = 0;
R[0] = 1;
memset(dpl, 0, sizeof(dpl));
memset(dpr, 0, sizeof(dpr));
for(int i = 1; i <= n; i++)
{
if(mx > i)
R[i] = min(R[2*p - i], mx - i);
else R[i] = 1;
while(s[i - R[i]] == s[i + R[i]])
R[i]++;
if(i + R[i] > mx)
{
//第一次mx更新的时候更新状态一定是最优的, i单调递增
for(int j = mx + 1; j <= i + R[i]; j++)
dpl[j] = i;//由于mx的移动距离最多是n, 复杂度是O(n)
mx = i + R[i], p = i;
}
}
p = mx = n + 1;
R[n + 1] = 1;//反过来从右边往左边再做一遍就行了
for(int i = n; i >= 1; i--)
{
if(mx < i)
R[i] = min(R[2*p - i], i - mx);
else R[i] = 1;
while(s[i - R[i]] == s[i + R[i]])
R[i]++;
if(i - R[i] < mx)
{
for(int j = mx - 1; j >= i - R[i]; j--)
dpr[j] = i;
mx = i - R[i], p = i;
}
}
return;
}
int main()
{
scanf("%s", in);
int n = strlen(in);
s[0] = '@';
for(int i = 0; i < n; i++)
s[2*i + 1] = in[i], s[2*i + 2] = '#';
s[2*n] = '$', s[2*n + 1] = '\0';
Manacher(s, R, 2*n);
int ans = 0;
for(int i = 2; i < 2*n; i += 2)//分隔符是插入的'#'字符
{
int len = (2*i - 2*dpl[i] + 1) / 2;
len += (2*dpr[i] - 2*i + 1) / 2;
ans = max(ans, len);
}
printf("%d\n", ans);
return 0;
}