题目链接:3790:神奇项链
处理出每个位置的最长的回文串,然后就是用最少的回文串覆盖整个区间
贪心一下,线段树维护即可,每次在左端点在合法区间里的回文串中找右端点最远的即可
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=210000;
const int inf=0x7fffffff/3;
int n,m,l[maxn],p[maxn],mx[maxn],ret,N=0;
char a[maxn],s[maxn];
struct segm{int l,r;}sg[maxn];
struct seg{
int l,r,v;
seg *lc,*rc;
};
seg *root=new seg();
void manacher(){
for (int i=1;i<=n;++i){
a[i<<1]=s[i];
a[(i<<1)+1]='#';
}
int mxid=0,id;
n<<=1; n+=2; a[0]='@'; a[1]='#'; a[n]='?';
for (int i=1;i<=n;++i){
if (mxid>i) p[i]=min(mxid-i,p[2*id-i]); else p[i]=1;
while (a[i+p[i]]==a[i-p[i]]) p[i]++;
if (p[i]+i>mxid) mxid=p[i]+i,id=i;
sg[++N].l=(i-p[i])/2+1; sg[N].r=(i+p[i])/2-1;
}
}
bool cmp(const segm &a,const segm &b){
return a.l<b.l||(a.l==b.l&&a.r<b.r);
}
void rebuild(seg *p,int l,int r){
p->l=l; p->r=r;
if (l+1==r){p->lc=p->rc=NULL;p->v=mx[l];return;}
else if (l+1<r){
int mid=(l+r)>>1;
p->lc=new seg();
p->rc=new seg();
if (l<mid) rebuild(p->lc,l,mid);
if (mid<r) rebuild(p->rc,mid,r);
p->v=max(p->lc->v,p->rc->v);
}
}
void ask(seg *p,int l,int r){
if (l<=p->l&&p->r<=r){ret=max(ret,p->v);return;}
int mid=(p->l+p->r)>>1;
if (l<mid) ask(p->lc,l,r);
if (mid<r) ask(p->rc,l,r);
}
int main(){
while (scanf("%s",s+1)!=EOF){
n=strlen(s+1);
for (int i=1;i<=n;++i) mx[i]=-inf;
N=0;
manacher();
n=strlen(s+1);
sort(sg+1,sg+N+1,cmp);
for (int i=1;i<=N;++i) mx[sg[i].l]=max(mx[sg[i].l],sg[i].r);
rebuild(root,1,n+1);
int pos=1,ans=0;
while (pos<=n){
ret=0; ask(root,1,pos+1);
ans++; pos=ret+1;
}
printf("%d\n",ans-1);
}
}