Description
母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字
母组成的字符串,每个小写字母表示一种颜色。为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。
Input
输入数据有多行,每行一个字符串,表示目标项链的样式。
Output
多行,每行一个答案表示最少需要使用第二个机器的次数。
Sample Input
abcdcba
abacada
abcdef
abacada
abcdef
Sample Output
0
2
5
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);
}
}