3790: 神奇项链
Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 551 Solved: 288
[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
分析
就是问最少用多少个回文串可以覆盖整个区间,最后ans-1就行了。
先跑一遍manacher,然后可以找到每个最长回文串的起点和终点(长的肯定比短的好)再贪心地选择最少的区间覆盖整个区间。
之前搜这道题看到树状数组优化……恐慌,结果贪心就能水了。。
贪心方法:先按起点从小到大排序,再选择起点在已覆盖的时间段内,终点最远的回文串。直至覆盖完全。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100010;
char s[N],s1[N];
int val[N];
int cnt=0;
inline int Min(int a,int b){
return a<b?a:b;
}
struct node{
int l,r;
}a[N];
void manacher(){
int len=strlen(s);
for(register int i=0;i<len;i++)
s1[++cnt]='#',s1[++cnt]=s[i];
s1[0]='+',s1[++cnt]='#';
int mx=0,id;
for(register int i=1;i<=cnt;i++){
if(mx>=i) val[i]=Min(mx-i+1,val[id*2-i]);
else val[i]=1;
while(s1[i-val[i]]==s1[i+val[i]]) val[i]++;
if(i+val[i]-1>mx) mx=i+val[i]-1,id=i;
a[i].l=i-val[i]+1,a[i].r=i+val[i]-1;
}
}
bool cmp(const node &a,const node &b){
return a.l<b.l||(a.l==b.l&&a.r<b.r);
}
int ti=1;
int ans=0;
#define ms(x,y) memset(x,y,sizeof(x))
void update(){
ans=0,ti=1,cnt=0;
ms(val,0);
}
int main(){
// freopen("test.txt","r",stdin);
while(scanf("%s",s)!=EOF){
update();
manacher();
//for(register int i=0;i<=cnt;i++) printf("%c",s1[i]);
// for(register int i=1;i<=cnt;i++)
// printf("%d %d\n",a[i].l,a[i].r);
sort(a+1,a+cnt+1,cmp);
for(register int i=1;i<=cnt;i++){
int x=0;
if(a[i].r<=ti) continue;
for(register int j=i;j<=cnt;j++){
if(a[j].l<=ti&&a[j].r>=a[x].r) x=j;
else if(a[j].l>ti) break;
}
ti=a[x].r;ans++;
if(ti>=cnt) break;
}
printf("%d\n",ans-1);
}
return 0;
}