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\)即可
#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即可
*/