题目大意:
定义双回文串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; }