SHTSC2011(SHOI) 双倍回文 一道用Manacher优化的动态维护题

题目大意

给你一个长度为 N 的字符串,求它的最长双倍回文字串。
双倍回文字串:记 x 倒置的串为 xR ,而双倍回文串是能表示成 x+xR+xR+x 形式的字符串。

N500000

解题思路

根据双倍回文串的可知它的长度一定是4的倍数,且由两个相同的有偶数个字符的回文串构成,我们可以先用Manacher与预处理出每一个位置的最长回文半径 Ri 。那么我们设这个串的对称轴是在第 p 个字符和第 p+1 个字符之间(即 p 为第一个 xR 结尾的位置), q 为第二个 xR 结尾的位置。那么为了满足双倍回文串的定义 qRq<=p q<=p+Rp/2 (这个画一画就能看出来)即只要满足这个条件的一对 p, q 就可以更新答案。

我们把 qRq 排个序,我们在枚举 p 时就维护满足条件一 q ,把它们加入队列。由于 p 递增,所以加到队列的 q 就不会出队了。因为答案等于 len(pq)4 ,所以我们只要在队列中找到 p+Rp/2 前面的第一个点就可以了找到对于当前 p 的最优解。这里可以用STL维护,也可以用并查集维护。

程序

这个代码为了简洁,用了STL库,跑的有点慢,跑全是 a 的极限数据可能会被卡。其实维护前面的第一个点可以用并查集来维护。

//SHTSC2011 Double Palindrome String YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>

using namespace std;

const int MAXN = 5e5 + 5;

set<int> D;

char S[MAXN];
int N, R[MAXN], Ord[MAXN];

bool cmp(int a, int b) { return a - R[a] < b - R[b];}

void Manacher() {
    int Max = 0, Id;
    for (int i = 1; i <= N; i ++) {
        R[i] = (Max >= i) ? min(Max - i, R[2 * Id - 1]) : 0;
        for (; S[i + R[i] + 1] == S[i - R[i]]; R[i] ++);
        if (R[i] + i > Max) Max = R[i] + i, Id = i; 
    }
}

void Prepare() {
    Manacher();
    for (int i = 1; i <= N; i ++) Ord[i] = i;
    sort(Ord + 1, Ord + 1 + N, cmp);
}

void Solve() {
    int Last = 0, Ans = 0;
    for (int i = 1; i <= N; i ++) {
        while (Last <= N && Ord[Last] - R[Ord[Last]] <= i) D.insert(Ord[Last ++]);
        set<int> :: iterator Side = D.upper_bound(i + R[i] / 2);
        if (Side == D.begin()) continue;
        Ans = max(Ans, *(--Side) - i);
    }
    printf("%d", Ans * 4);
}

bool Check() {
    for (int i = 2; i <= N; i ++) 
        if (S[i] != S[i - 1]) return 0;
    printf("%d", N / 4 * 4);
    return 1;
}   

int main() {
    scanf("%d", &N);
    scanf("%s", S + 1);
    if (Check()) return 0;
    Prepare();
    Solve();
}

你可能感兴趣的:(SHTSC2011(SHOI) 双倍回文 一道用Manacher优化的动态维护题)