BZOJ3790. 神奇項鍊

處理出原串中每一個點爲中點的極長迴文串,這個用hash或者manacher都可,反正這裏不是複雜度瓶頸:(。那麼問題就變成了可重疊的線段覆蓋問題。
\(f[i]\) 表示已經徹底覆蓋 \(1-i\) 的最小代價
那麼取結束點在i的線段\([l,r](r == i)\)
\(f[i] = \min\{f[k] + 1\} (l - 1 \leq k < r)\)
用bit維護後綴\(\min\)便可c++

#include <bits/stdc++.h>
using namespace std;

typedef unsigned int uint;
const int N = 100010;
const uint base = 13131;

char s[N];
uint h[N], pw[N], g[N];
int p[N], f[N], cnt, n;
struct Node {int l, r;} a[N];
struct bit {
    int c[N];
    #define lowbit(i) (i & -i)
    void clear() {
        memset(c, 0x3f, sizeof(c));
    }
    void add(int x, int v) {
        for(int i = x; i; i -= lowbit(i)) c[i] = min(c[i], v);
    }
    int query(int x) {
        int ans = 0x3f3f3f3f;
        for(int i = x; i <= n + 1; i += lowbit(i)) ans = min(ans, c[i]);
        return ans;
    }
} t;

uint gethash(int l, int r) {
    return h[r] - h[l - 1] * pw[r - l + 1];
}
uint getghash(int l, int r) {
    return g[l] - g[r + 1] * pw[r - l + 1];
}

bool check(int l, int r) {
    return gethash(l, r) == getghash(l, r);
}

bool operator < (Node a, Node b) {
    return a.r < b.r;
}

int solve() {
    t.clear();
    memset(f, 0x3f, sizeof(f));
    sort(a + 1, a + cnt + 1);
    int cur = 1;
    t.add(1, 0);
    for(int i = 1; i <= n; ++i) {
        while(cur <= cnt && a[cur].r <= i) f[i] = min(f[i], t.query(a[cur++].l) + 1);
        t.add(i + 1, f[i]);
    }
    return f[n] - 1;
}

int main() {
#ifndef ONLINE_JUDGE
freopen("data.in","r",stdin);
#endif  
    while(~scanf("%s", s + 1)) {
        n = strlen(s + 1);
        pw[0] = 1;
        h[0] = g[n + 1] = 0;
        for(int i = 1; i <= n; ++i) {
            h[i] = h[i - 1] * base + s[i];
            pw[i] = pw[i - 1] * base;
        }
        for(int i = n; i; --i) g[i] = g[i + 1] * base + s[i];
        cnt = 0;
        for(int i = 1; i <= n; ++i) {
            p[i] = 0;
            int l = 1, r = min(i - 1, n - i);
            while(l <= r) {
                int mid = (l + r) >> 1;
                if(check(i - mid, i + mid)) l = mid + 1, p[i] = mid;
                else r = mid - 1;
            }
            a[++cnt] = (Node) {i - p[i], i + p[i]};
            p[i] = 0;
            l = 0, r = min(i, n - i);
            while(l <= r) {
                int mid = (l + r) >> 1;
                if(check(i - mid + 1, i + mid)) l = mid + 1, p[i] = mid;
                else r = mid - 1;
            }
            if(p[i]) a[++cnt] = (Node) {i - p[i] + 1, i + p[i]};
        }
        printf("%d\n", solve());
    }
}
/*
處理出原串中每一個點爲中點的極長迴文串,那麼問題就變成了可重疊的線段覆蓋問題。
設 $f[i]$ 表示已經徹底覆蓋 $1-i$ 的最小代價
那麼取結束點在i的線段[l,r](r == i)
f[i] = min{f[k] + 1} (l - 1 <= k < r)
用bit維護後綴min便可
*/
相關文章
相關標籤/搜索