【题目链接】
【思路要点】
- 用Manacher算法求出每个字符为中心的极长回文子串,显然,选取更短的回文子串不会使答案更优。
- 因此,问题转化成了“有若干个区间,至少选取多少个才能使它们的并是\([1,N]\)”。
- 用线段树优化DP即可。
- 时间复杂度\(O(NLogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; #define MAXN 200005 #define INF 1e9 struct SegMentTree { struct node { int lc, rc; int minnum; }; node a[MAXN]; int size, n; private: void build(int now, int l, int r) { a[now].minnum = 0; if (l == r) return; int mid = (l+r)/2; a[now].lc = ++size; build(size, l, mid); a[now].rc = ++size; build(size, mid+1, r); } void update(int now) { if (a[now].lc == 0) return; a[now].minnum = min(a[a[now].lc].minnum, a[a[now].rc].minnum); } void maintain(int now, int l, int r, int pos, int x) { if (l == r) {a[now].minnum = x; return; } int mid = (l+r)/2; if (mid >= pos) maintain(a[now].lc, l, mid, pos, x); else maintain(a[now].rc, mid+1, r, pos, x); update(now); } int query(int now, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[now].minnum; int mid = (l+r)/2, ans = INF; if (mid >= ql) ans = min(ans, query(a[now].lc, l, mid, ql, min(qr, mid))); if (mid+1 <= qr) ans = min(ans, query(a[now].rc, mid+1, r, max(mid+1, ql), qr)); return ans; } public: void init(int x) { n = x; size = 0; build(size, 0, n); } void insert(int pos, int x) { maintain(0, 0, n, pos, x); } int ask(int x, int y) { if (x>y) return INF; else return query(0, 0, n, x, y); } }; char s[MAXN], x[MAXN]; int len[MAXN], opt[MAXN]; vector <int> seg[MAXN]; SegMentTree T; int main() { while (scanf("%s", s+1) != EOF) { int n = strlen(s+1); x[0] = '$'; x[1] = '#'; x[2*n+2] = '%'; for (int i = 1; i <= n; i++) {x[i*2] = s[i]; x[i*2+1] = '#'; } int right = 0, pos = 0; for (int i = 1; i <= 2*n+1; i++) { if (i <= right) { int j = pos*2-i; if (len[j]<right-i+1) len[i] = len[j]; else { len[i] = right-i+1; while (x[i+len[i]] == x[i-len[i]]) len[i]++; right = i+len[i]-1; pos = i; } } else { len[i] = 1; while (x[i+len[i]] == x[i-len[i]]) len[i]++; right = i+len[i]-1; pos = i; } } for (int i = 1; i <= 2*n+1; i++) seg[i].clear(); for (int i = 1; i <= 2*n+1; i++) seg[i+len[i]-1].push_back(i-len[i]+1); T.init(2*n+1); for (int i = 1; i <= 2*n+1; i++) { opt[i] = i; for (int j = 0; j<seg[i].size(); j++) opt[i] = min(opt[i], T.ask(seg[i][j]-1, i-1)+1); T.insert(i, opt[i]); } printf("%d\n", opt[2*n+1]-1); } return 0; }