码迷,mamicode.com
首页 > 其他好文 > 详细

[BZOJ3790] 神奇项链

时间:2017-10-16 23:36:19      阅读:389      评论:0      收藏:0      [点我收藏+]

标签:efi   ref   href   geo   ffffff   pre   div   using   def   

想要成为我的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] 神奇项链

标签:efi   ref   href   geo   ffffff   pre   div   using   def   

原文地址:http://www.cnblogs.com/zzyh/p/7679089.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!