【BZOJ3790】神奇项链

【题目链接】

【思路要点】

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值