[BZOJ2342][Shoi2011]双倍回文
试题描述
输入
输入分为两行,第一行为一个整数 n,表示字符串的长度,第二行有 n 个连续的小写的英文字符,表示字符串的内容。
输出
输出文件只有一行,即:输入数据中字符串的最长双倍回文子串的长度,如果双倍回文子串不存在,则输出0。
输入示例
16 ggabaabaabaaball
输出示例
12
数据规模及约定
N<=500000
题解
对于一个形如 WRWWRW 的串,我们不妨考虑它的四分之一。
先用 manacher 求出 P[i] 数组,对于一个子串 str(l, r)(原串从位置 l 到位置 r 的连续部分)满足能成为一个双回文串的四分之一当且仅当 l + P[l] - 1 ≤ r 且 r - P[r] / 2 + 1 ≤ l,于是我们可以把每个位置 r 按照 r + P[r] - 1 从大到小排序,然后平衡树(或 set)维护一下当前所有大于等于 r + P[r] - 1 的位置,然后找到大于等于 r - P[r] / 2 + 1 的最小数就是最优的 l,那么就可以用 r - l + 1 更新答案啦。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 1000010 #define oo 2147483647 struct Node { int v, r; Node() {} Node(int _, int __): v(_), r(__) {} } ns[maxn]; int rt, ToT, fa[maxn], ch[maxn][2]; void rotate(int u) { int y = fa[u], z = fa[y], l = 0, r = 1; if(z) ch[z][ch[z][1]==y] = u; if(ch[y][1] == u) swap(l, r); fa[u] = z; fa[y] = u; fa[ch[u][r]] = y; ch[y][l] = ch[u][r]; ch[u][r] = y; return ; } void insert(int& o, int v) { if(!o) { ns[o = ++ToT] = Node(v, rand()); return ; } bool d = v > ns[o].v; insert(ch[o][d], v); fa[ch[o][d]] = o; if(ns[ch[o][d]].r > ns[o].r) { int t = ch[o][d]; rotate(t); o = t; } return ; } int qupp(int o, int v) { if(!o) return oo; if(ns[o].v == v) return v; if(v > ns[o].v) return qupp(ch[o][1], v); return min(ns[o].v, qupp(ch[o][0], v)); } char Str[maxn], S[maxn]; int n, Len[maxn], alp[maxn], pos[maxn]; bool cmp(int a, int b) { return a + Len[a] - 1 > b + Len[b] - 1; } int main() { // freopen("data.in", "r", stdin); n = read(); scanf("%s", Str + 1); for(int i = 1; i <= n; i++) S[i<<1] = Str[i], S[(i<<1)-1] = '#'; n = n << 1 | 1; S[n] = '#'; for(int i = 1; i <= n; i++) alp[i] = alp[i-1] + ('a' <= S[i] && S[i] <= 'z'); int mxi = 0; for(int i = 1; i <= n; i++) { int mxp = mxi + Len[mxi] - 1; if(mxp < i) Len[i] = 1; else Len[i] = min(Len[(mxi<<1)-i], mxp - i + 1); while(1 <= i - Len[i] + 1 && i + Len[i] - 1 <= n && S[i-Len[i]+1] == S[i+Len[i]-1]) Len[i]++; Len[i]--; if(i + Len[i] - 1 > mxp) mxi = i; } // for(int i = 1; i <= n; i++) printf("%d%c", Len[i], i < n ? ' ' : ' '); for(int i = 1; i <= n; i++) pos[i] = i; sort(pos + 1, pos + n + 1, cmp); int ans = 0; for(int i = n, j = 1; i; i--) { while(j <= n && pos[j] + Len[pos[j]] - 1 >= i) { if(S[pos[j]] == '#') insert(rt, pos[j]); j++; } if(S[i] != '#') continue; int t = qupp(rt, i - (Len[i] + 1 >> 1) + 1); ans = max(ans, alp[i] - alp[t-1] << 2); } printf("%d ", ans); return 0; }