【bzoj3790】【神奇项链】【manacher+dp+树状数组】

6 篇文章 0 订阅
2 篇文章 0 订阅

Description

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

Input

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

Output

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

Sample Input

abcdcba
abacada
abcdef

Sample Output

0
2
5

HINT

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

每行的字符串长度小于等于 50000 
题解:
           首先用manacher处理出每个点最大的回文串.
           然后就是用最少的可重叠的线段覆盖整个区间.
           把所有线段按右端点排序.
           设f[i]表示到第i条线段,覆盖到这个区间右端点用的最小线段数目.
           显然 f[i]=min(f[j]+1)(seg[j].r>=seg[i].l-1)
           直接dp是O(n^2)的.用线段树可以优化到O(n log n);
           也可以把树状数组反过来,这样比较好写.
代码:
   
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50010
#define inf 2100000000
using namespace std;
int p[N<<1],c[N],len,num,ans;
char ch[N],s[N<<1];
struct use{int l,r;}a[N<<1];
bool cmp(use a,use b){return a.r<b.r;}
int lowbit(int x){return x&(-x);}
void add(int x,int v){
  for (int i=x;i;i-=lowbit(i))
    c[i]=min(c[i],v);	  
}
int query(int x){
  int mn=inf;
  if (x==0) return 0;
  for (int i=x;i<=len;i+=lowbit(i))
    mn=min(mn,c[i]);
  return mn;	
}
void make(int x,int y){
  x=x/2+1;y=y/2-1;
  if (x>y) return;
  a[++num].l=x;a[num].r=y;
}
void manacher(){
  int m=2*len+1;
  for (int i=1;i<=len;i++){
    s[i<<1]=ch[i];
    s[i<<1|1]='#';
  } 	
  s[0]='+';s[1]='#';s[m+1]='-';
  int mx=0,id;
  for (int i=1;i<=m;i++){
    if (mx>i) p[i]=min(p[2*id-i],mx-i);
    else p[i]=1;
    for (;s[i-p[i]]==s[i+p[i]];p[i]++);
    make(i-p[i],i+p[i]);
    if (i+p[i]>mx) mx=p[i]+i,id=i;
  } 
}
int main(){
  while (scanf("%s",ch+1)!=EOF){
  	ans=inf;num=0;memset(c,127/3,sizeof(c));
	len=strlen(ch+1);manacher();
	sort(a+1,a+num+1,cmp);
    for (int i=1;i<=num;i++){
      int x=query(a[i].l-1)+1;
      add(a[i].r,x);
      if (a[i].r==len) ans=min(ans,x);
    }
    printf("%d\n",ans-1);
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值