一道很有意思的题目,活用了最长回文子串算法Manacher算法和最少区间覆盖。
最少区间覆盖:其实就是把一堆一小段区间按左边排序,区间数sum=0;然后把其中覆盖进(1,n)的区间中,开始先找其实为1的区间,找到最远的点t,找到一次,区间数sum加1,
把t赋值给tt,在把1值t,然后就不断循环找出一个区间起始点在【t+2,tt+1】的区间,找到能延伸到最远的一点tt‘,然后把tt赋值给t,然后把tt’赋值给tt,每找到一次区间数sum就加1,然后继续循环。直到tt=n位置就结束,得到最少区间数。好吧,我也感觉我说不明白= =,还是建议你百度一下吧。
Manacher算法:百度就有了。
题目传送门:http://acm.zju.edu.cn/onlinejudge/showRuns.do?contestId=1&problemCode=3296&judgeReplyIds=5
题目意思:给你一个字符串,然后让你用这个字符串处理出来的回文串,拼接成之前给出的字符串,求最少的拼接次数。
做法:先用Manacher算法处理出回文子串的区间,然后用最少区间覆盖,得出最少区间覆盖的值-1就可以得出最少拼接数。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define M 100050
char str1[M],str[2*M];
int rad[M],nn,n;
struct st
{
int s,t;
}a[100010];
int cmp(const st &b,const st &c)
{
if(b.s<c.s)
{
return 1;
}
return 0;
}
void Manacher(int *rad,char *str,int n)
{
int i;
int mx = 0;
int id;
for(i=1; i<n; i++)
{
if( mx > i )
rad[i] = rad[2*id-i]<mx-i?rad[2*id-i]:mx-i;
else
rad[i] = 1;
for(; str[i+rad[i]] == str[i-rad[i]]; rad[i]++)
;
if( rad[i] + i > mx )
{
mx = rad[i] + i;
id = i;
}
}
}
int main()
{
int i,ans,Case=1,j;
while(scanf("%s",str1)!=EOF)
{
nn=strlen(str1);
n=2*nn+2;
str[0]='$';
for(i=0;i<=nn;i++)
{
str[2*i+1]='#';
str[2*i+2]=str1[i];
}
// printf("%s\n",str);
Manacher(rad,str,n);
ans=1;
int k=0;
//处理出回文子串区间。
for(i=2;i<=n;i++)
{
rad[i]--;
//printf("%d %d %c\n",i,rad[i],str[i]);
if(rad[i]==0)
continue;
if(str[i]=='#')
{
if((rad[i])%2==1)
{
a[++k].s=(i-(rad[i]))/2;
a[k].t=(i+(rad[i]))/2;
}
else
{
a[++k].s=(i-(rad[i])+1)/2;
a[k].t=(i+(rad[i])-1)/2;
}
}
else if(str[i]>='a'&&str[i]<='z')
{
if((rad[i])%2==0)
{
a[++k].s=(i-(rad[i]))/2;
a[k].t=(i+(rad[i]))/2;
}
else
{
a[++k].s=(i-(rad[i])+1)/2;
a[k].t=(i+(rad[i])-1)/2;
}
}
}
/*for(i=1;i<=k;i++)
{
printf("%d %d \n",a[i].s,a[i].t);
}*/
//以下为最少区间覆盖。
sort(a+1,a+k+1,cmp);
int end=0,sta=1,sum=1;
i=1;
while(i<=k&&a[i].s==sta)
{
if(a[i].t>end)
end=a[i].t;
i++;
}
while(end!=nn)
{
sta=end+1;
j=end;
while(i<=k&&a[i].s<=sta)
{
if(a[i].t>j)
j=a[i].t;
i++;
}
end=j;
sum++;
}
printf("%d\n",sum-1);
}
return 0;
}