您的位置:首页 > 其它

[BZOJ]3790 神奇项链 Manacher+树状数组

2017-07-28 12:12 375 查看

3790: 神奇项链

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 548  Solved: 286

[Submit][Status][Discuss]

Description

母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字
母组成的字符串,每个小写字母表示一种颜色。为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。 

Input

输入数据有多行,每行一个字符串,表示目标项链的样式。 

Output

多行,每行一个答案表示最少需要使用第二个机器的次数。 

Sample Input

abcdcba

abacada

abcdef

Sample Output

0

2

5

HINT

每个测试数据,输入不超过 5行 

每行的字符串长度小于等于 50000 

Source



[Submit][Status][Discuss]

HOME Back

题解

        这道题当我们用Manacher求出极大回文子串之后,问题,也就变成了询问最小线段数覆盖全区间,注意这里线段可以有交集.我们考虑dp贪心的思想,每个点的最优策略用值域树状数组来维护,f[i]表示1到i这个区间最少用多少线段覆盖,f[i]=min{f[j]}
+1,其中f[j]的右端点要大于等于i的左端点,这样才能覆盖.
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int inf=1000000010;
char ss[maxn],s[maxn*2];
int c[maxn],pal[maxn*2],m,n,ans,num;
struct query{
int l,r;
}q[maxn];
inline bool cmp(query a,query b){return a.r<b.r;}
inline void init(){
num=0;n=strlen(ss+1);m=2*n+1;ans=inf;
for(int i=1;i<=n;i++) s[i*2]=ss[i],s[i*2+1]='#';
s[0]='+',s[1]='#',s[m+1]='-';
}
inline void add(int l,int r){
l=l/2+1,r=r/2-1;
if(l>r) return;
q[++num].l=l,q[num].r=r;
}
inline void manacher(){
int mx=0,id=0;
for(int i=1;i<=m;i++){
if(mx>i) pal[i]=min(mx-i,pal[2*id-i]);
else pal[i]=1;
while(s[i-pal[i]]==s[i+pal[i]]) pal[i]++;
add(i-pal[i],i+pal[i]);
if(i+pal[i]>mx) mx=i+pal[i],id=i;
}
}
inline void modify(int i,int x){
if(!i) return;
for(;i;i-=i&-i) c[i]=min(c[i],x);
}
inline int query(int i){
if(!i) return 0;
int tmp=inf;
for(;i<=n;i+=i&-i) tmp=min(tmp,c[i]);
return tmp;
}
int main(){
while(scanf("%s",ss+1)!=EOF){
init();
manacher();
for(int i=1;i<=n;i++) c[i]=inf;
for(int i=1;i<=num;i++){
int x=query(q[i].l-1)+1;
modify(q[i].r,x);
if(q[i].r==n) ans=min(ans,x);
}
printf("%d\n",ans-1);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: