BZOJ3790 神奇项链
权限题...
描述:
Description
母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字
母组成的字符串,每个小写字母表示一种颜色。为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。
Input
输入数据有多行,每行一个字符串,表示目标项链的样式。
Output
多行,每行一个答案表示最少需要使用第二个机器的次数。
Sample Input
abcdcba
abacada
abcdef
Sample Output
0
2
5
Hint
每个测试数据,输入不超过 5行
每行的字符串长度小于等于 50000
题目就是问给定的串最少是由多少个回文串覆盖构成的
关于回文串首先肯定想的是 manacher
用 manacher 可以求出每个回文串
题目问的是给定的串最少是由多少个回文串覆盖构成的
那就变成了经典的线段覆盖问题
贪心一波就好了:
开一个数组 lin[ i ] 表示从第 i 个字符开始的回文串的最长长度
首先第一个字符一定要被覆盖到
那么就选 1~lin[ 1 ] 的线段来覆盖
然后考虑lin[ 1 ] + 1 的点一定要被覆盖到
枚举 2~lin[ 1 ] +1 开头的所有线段,找到右端点最右的线段并选择
然后考虑更后面的点,用同样的方法贪心即可
这么显然的贪心应该不用证明吧
复杂度 O(n)
然后答案就是选择的线段数量-1
代码:
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; const int N=2e5+7;; char s[N],a[N]; int f[N],ans; int lin[N];//注意此时的lin[i]是在原串时下标i void manacher() { int len=strlen(s+1); len=len*2+1; a[0]='$'; for(int i=1;i<=len;i++) a[i]= (i&1) ? '#' : s[i>>1]; int pos=0,mx=0; for(int i=1;i<=len;i++) { f[i]= i<mx ? min(f[(pos<<1)-i],mx-i) : 1; while(a[i+f[i]]==a[i-f[i]]) f[i]++; if(i+f[i]>mx) mx=i+f[i],pos=i; } } void slove() { int ans=0,mx=0,pos=0,len=strlen(s+1); for(int i=2;i<=len<<1;i++) { pos=(i>>1)-(f[i]>>1)+1; lin[pos]=max(lin[pos],f[i]-1);//计算出lin数组 } int i=1; while(i<=len) { mx=max(mx,i+lin[i]-1); pos=mx; for(int j=i+1;j<=pos;j++) mx=max(mx,j+lin[j]-1);//贪心 ans++; i=pos+1; } printf("%d\n",ans-1);//线段数量-1 } int main() { while(scanf("%s",s+1)!=EOF) { memset(f,0,sizeof(f)); memset(a,0,sizeof(a)); memset(lin,0,sizeof(lin)); manacher(); slove(); } return 0; }
当然因为数据不大,所以 manacher 可以换成哈希
枚举中心然后二分可能长度,复杂度O(nlogn)
代码就不贴了