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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章