BZOJ3790:神奇项链

版权声明:蒟蒻Blog随意转载 https://blog.csdn.net/a1799342217/article/details/82946002

Manacher 贪心

题目传送门

题目大意: 给你一个字符串,你可以造任意的回文串并把它们拼起来得到原串,求最少拼接次数(回文串可重叠)。

先Manacher求出 p p 数组,把每个点看成一个一条线段,左右端点分别为 i p [ i ] i-p[i] i + p [ i ] i+p[i] 。然后就变成线段覆盖问题(用最少的线段覆盖住整个区间),贪心即可(我懒就用了堆)。

代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define F inline
using namespace std;
struct node{ int l,r; };
int id,r,ans,p[N]; char s[N],s1[N];
F bool operator < (node a,node b){ return a.l>b.l; }
priority_queue <node> q;
int main(){
	while (scanf("%s",s)!=EOF){
		memset(s1,0,sizeof(s1)),s1[0]='$';
		for (int i=0;s[i];i++) s1[2*i+1]='#',s1[2*i+2]=s[i];
		s1[strlen(s1)]='#',memset(p,0,sizeof(p)),id=r=ans=0;
		for (int i=1;s1[i];i++){
			p[i]=r>i?min(p[id*2-i],r-i):1;
			while (s1[i+p[i]]==s1[i-p[i]]) p[i]++;
			if (p[i]+i>r) r=p[i]+i,id=i;
		}
		while (!q.empty()) q.pop(); int len=strlen(s1)-1;
		for (int i=2;i<len;i++) q.push((node){i-p[i]+1,i+p[i]-1});
		for (int lst=1,now=0;lst<len;ans++,lst=now)
			while (!q.empty()&&q.top().l<=lst) now=max(now,q.top().r),q.pop();
		printf("%d\n",ans-1);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/82946002
今日推荐