1. 程式人生 > >[BZOJ3790] 神奇項鏈

[BZOJ3790] 神奇項鏈

ems bzoj3790 關鍵字排序 std space 關鍵字 字符 cstring bsp

想要成為我的master嘛?

題目大意:用最少的回文串覆蓋整個字符串,可重疊。

題解:Manacher+貪心

md最近好幾個線段覆蓋的題都沒看出來。

Manacher算出以每個字符為中心的回文串,就是一個線段,計算出左端點i-Len[i]+1和

右端點i+Len[i]-1,然後貪心用每個線段覆蓋區間就好了,兩個回文串,就是兩個區間是

可以重疊的。貪心嘛,就是以左端點為第一關鍵字,右端點為第二關鍵字排序。沒次找

能與上一條線段相接的右端點最大的就行了。

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include
<algorithm> #define maxn 50009 using namespace std; char s[maxn*2],str[maxn*2]; int len,Len[maxn*2]; struct X{ int l,r; }xd[maxn*2]; bool cmp(X a,X b){ if(a.l!=b.l)return a.l<b.l; else return a.r>b.r; } void getstr(){ int k=0;str[k++]=$; for(int i=0;i<len;i++){ str[k
++]=#;str[k++]=s[i]; } str[k++]=#;len=k; } void Manacher(){ int mx=0,id; getstr();memset(Len,0,sizeof(Len)); for(int i=1;i<len;i++){ if(mx>i)Len[i]=min(Len[2*id-i],mx-i); else Len[i]=1; while(str[i+Len[i]]==str[i-Len[i]])Len[i]++; if(i+Len[i]>mx)mx=i+Len[i],id=i; } }
int main(){ while(scanf("%s",&s)!=EOF){ len=strlen(s); Manacher(); for(int i=1;i<len;i++){ xd[i].l=i-Len[i]+1;xd[i].r=i+Len[i]-1; } sort(xd+1,xd+len,cmp); int ans=1,now=2,r=xd[1].r; while(r!=len-1){ int mx=r; while(xd[now].l<=r&&now<len){ mx=max(mx,xd[now].r);now++; } r=mx;ans++; } cout<<ans-1<<endl; } return 0; }

[BZOJ3790] 神奇項鏈