首页 > 代码库 > [BZOJ2342][Shoi2011]双倍回文

[BZOJ2342][Shoi2011]双倍回文

[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 ? ‘ ‘ : ‘\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\n", ans);
     
    return 0;
}

 

[BZOJ2342][Shoi2011]双倍回文