luoguP4555 [国家集训队]最长双回文串 manacher算法

不算很难的一道题吧....

很容易想到枚举断点,之后需要处理出以$i$为开头的最长回文串的长度和以$i$为结尾的最长回文串的长度

分别记为$L[i]$和$R[i]$

由于求$R[i]$相当于把$L[i]$反过来求一遍,因此只需考虑求$L[i]$

考虑$manacher$算法

我们注意到,当$mr$扩展时,第一个把$mr$扩展到$i$的中心$j$构成的串就是$L[i]$

在$manacher$算法中统计一下即可

复杂度$O(n)$

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define ll long long
#define ri register int
#define rep(io, st, ed) for(ri io = st; io <= ed; io ++)
#define drep(io, ed, st) for(ri io = ed; io >= st; io --)
    
const int sid = 2e5 + 1e4;

char s[sid], t[sid];
int n, m, r[sid], L[sid], R[sid];

void manacher(char *s, int *lst, int opt) {
    r[1] = 1; lst[1] = 1;
    int mr = 1, pos = 1;
    rep(i, 2, m) {
        r[i] = min(mr - i + 1, r[pos + pos - i]);
        while(i - r[i] > 0 && s[i + r[i]] == s[i - r[i]]) 
            lst[i + r[i]] = 2 * r[i] + 1, r[i] ++;
        if(i + r[i] - 1 > mr) mr = i + r[i] - 1, pos = i;
    }
    if(opt) reverse(lst + 1, lst + m + 1);
}

int main() {
    scanf("%s", s + 1);
    n = strlen(s + 1);
    
    rep(i, 1, n) t[++ m] = '#', t[++ m] = s[i];
    t[++ m] = '#';
    
    
    reverse(t + 1, t + m + 1);
    manacher(t, R, 1);
    reverse(t + 1, t + m + 1);
    manacher(t, L, 0);

    int ans = 0;
    rep(i, 1, m)
        if(t[i] == '#') 
            ans = max(ans, (L[i] + R[i] - 1) / 2);
    printf("%d
", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/reverymoon/p/9964653.html