HDU 5371 Hotaru's problem Manacher+线段树

链接

题解来源:点击打开链接

给出一个n长的序列

要求序列中最长的子串使得子串满足ABA的形式

B = reverse(A) , 就是A的翻转。

如 1 1 1, 12 21 12, 321 123 321

问:最长的长度是多少

思路:

因为AB就是一个偶数长度的回文,所以显然是先跑一个Manacher 

如:

1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10

3 - 2 - 1 - 1 - 2 - 3 - 3 - 2 - 1 - 4  <--input
  0 - 0 - 3 -  0 - 0 - 3 - 0 - 0 - 0 -  <--dp 表示这个间隙的回文长度

处理出dp数组表示每个数字间隙间的最长回文长度。

考虑间隙 3-4, 若3-4是A - B 部分的间隙,则B - A 部分的间隙可能是 4-5, 5-6, 6-7

假设间隙是j ,那么 j 点的回文长度必须覆盖i点,即:dp[j] >= j - i

变形一下: i >= j - dp[j]

若j能成为答案,则答案为 (j-i)*3, 所以我们要找到满足 i >= j-dp[j] 中最大的j值

因为对于不等号右边是关于j的表达式,所以用线段树维护 j-dp[j]的最小值,每次找区间内满足条件的最右点。

还有一个细节,dp[j]  显然是要>0 的,所以当dp[j] ==0时要让dp[j] = -inf,即 j-dp[j] = inf,防止j点更新答案。。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

template <class T>
inline bool rd(T &ret) {
    char c; int sgn;
    if (c = getchar(), c == EOF) return false;
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return true;
}

typedef long long ll;
const int MAX_N = 250007;
const int inf = 1e8;
int n;
int str[MAX_N], s[MAX_N << 1];
int p[MAX_N << 1], dp[MAX_N << 1];

#define lson idx << 1
#define rson idx << 1 | 1

struct Node {
    int l, r;
    int mi, ma;
    Node() {}
    Node(int _l, int _r, int _mi, int _ma) {
        l = _l, r = _r, mi = _mi, ma = _ma;
    }
};

struct Segment {
    Node tree[MAX_N << 1];
    void up(int idx) {
        tree[idx].ma = max(tree[lson].ma, tree[rson].ma);
        tree[idx].mi = min(tree[lson].mi, tree[rson].mi);
    }
    void build(int idx, int L, int R) {
        tree[idx] = Node(L, R, 0, 0);
        if (L == R) {
            tree[idx].ma = dp[L];
            tree[idx].mi = dp[L];
            return;
        }
        int mid = (L + R) >> 1;
        build(lson, L, mid);
        build(rson, mid + 1, R);
        up(idx);
    }
    int query(int l, int r, int idx, int val) {
        if (tree[idx].l == tree[idx].r) {
            return tree[idx].r;
        }
        int mid = (tree[idx].l + tree[idx].r) >> 1;
        if (r <= mid) {
            if (tree[lson].mi <= val)return query(l, r, lson, val);
        }
        else if (mid < l) {
            if (tree[rson].mi <= val) return query(l, r, rson, val);
        }
        else {
            int lMin = tree[lson].mi, rMin = tree[rson].mi;
            if (rMin <= val) return query(mid + 1, r, rson, val);
            else if (lMin <= val) return query(l, mid, lson, val);
        }
        return -1;
    }
} Seg;

int Manacher() {
    int cnt = 0;
    s[cnt++] = -2, s[cnt++] = -1;
    for (int i = 0; i < n; ++i) {
        s[cnt++] = str[i];
        s[cnt++] = -1;
    }
    memset(p, 0, sizeof p);
    int mx = 0, id = 0;
    for (int i = 1; i < cnt; ++i) {
        if (mx > i) p[i] = min(p[2 * id - i], mx - i);
        else p[i] = 1;
        while (i - p[i] > 0 && i + p[i] < cnt && s[i - p[i]] == s[i + p[i]]) ++p[i];
        if (i + p[i] > mx) {
            mx = i + p[i];
            id = i;
        }
    }
    for (int i = 1; i < cnt; ++i) {
        if (s[i] == -1) {
            int id = i >> 1;
            dp[id] = id - (p[i] - 1) / 2;
            if ((p[i] - 1) / 2 == 0)dp[id] = inf;
        }
    }
    Seg.build(1, 1, n - 1);
    int ans = 0;
    for (int i = 1; i < n; ++i) {
        if (i - dp[i] >= 1 && i + 1 <= min(i + i - dp[i], n - 1))
            ans = max(ans, Seg.query(i + 1, min(i + i - dp[i], n-1), 1, i) - i);
    }
    return ans;
}

int main() {
    int T;
    int cas = 0;
    scanf("%d", &T);
    while (T-- > 0) {
        rd(n);
        for (int i = 0; i < n; ++i) rd(str[i]);
        int now;
        if (n < 3)now = 0;
        else now = Manacher() * 3;
        printf("Case #%d: %d\n", ++cas, now);
    }
    return 0;
}



你可能感兴趣的:(HDU 5371 Hotaru's problem Manacher+线段树)