[BZOJ 4310]跳蚤
[BZOJ 4310]跳蚤
题目
很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k 个子串中选择字典序最大的那一个。他称其为“魔力串”。现在他想找一个最优的分法让“魔力串”字典序最小。INPUT
第一行一个整数 k。接下来一个长度不超过 105 的字符串 S。OUTPUT
输出一行,表示字典序最小的“魔力串”。
SAMPLE
INPUT
13
bcbcbacbbbbbabbacbcbacbbababaabbbaabacacbbbccaccbcaabcacbacbcabaacbccbbcbcbacccbcccbbcaacabacaaaaabaOUTPUT
cbc
解题报告
BZOJ上榜留念2333(我TM打了一个下午)
首先我们看到题目要求的是最大值最小,那么我们考虑二分
我们很容易知道,所有本质不同的字串数为$\sum_{i}^{len}len-sa[i]-height[i]$
(这个很容易由SA与height的含义推得,这里就不再展开证明了)
那么我们二分第$mid$个字串,去判断这个字串是否满足条件
判断就很好判断了(虽然我TM就是在这调了一下午),我们利用贪心的思想,从后向前枚举,然后判断是否要分开,最后判断分开次数与$k$的关系即可
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 using namespace std; 6 char s[100005]; 7 int n,m; 8 int t1[100005],t2[100005],t3[100005],buc[100005]; 9 int sa[100005],rank[100005],height[100005]; 10 inline void Suffix(){ 11 int i,j,k(0),p(0),*x(t1),*y(t2),*t; 12 for(i=0;i<=m;++i)buc[i]=0; 13 for(i=1;i<=n;++i)++buc[x[i]=s[i]]; 14 for(i=1;i<=m;++i)buc[i]+=buc[i-1]; 15 for(i=n;i>=1;--i)sa[buc[x[i]]--]=i; 16 for(j=1;p<n;j<<=1,m=p){ 17 for(p=0,i=n-j+1;i<=n;++i)y[++p]=i; 18 for(i=1;i<=n;++i) 19 if(sa[i]>j) 20 y[++p]=sa[i]-j; 21 for(i=0;i<=m;++i)buc[i]=0; 22 for(i=1;i<=n;++i)t3[i]=x[y[i]]; 23 for(i=1;i<=n;++i)++buc[t3[i]]; 24 for(i=1;i<=m;++i)buc[i]+=buc[i-1]; 25 for(i=n;i>=1;--i)sa[buc[t3[i]]--]=y[i]; 26 for(t=x,x=y,y=t,x[sa[1]]=1,p=1,i=2;i<=n;++i) 27 x[sa[i]]=((y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+j]==y[sa[i-1]+j]))?p:++p; 28 } 29 for(i=1;i<=n;++i)rank[sa[i]]=i; 30 for(i=1;i<=n;height[rank[i++]]=k) 31 for(k?--k:0,j=sa[rank[i]-1];s[i+k]==s[j+k];++k); 32 } 33 int k; 34 typedef long long L; 35 L tot; 36 int ansl,ansr,ll,rr; 37 /*inline void get_kth(L x){ 38 for(int i=1;i<=n;++i){ 39 if(x>(L)(n-sa[i]-height[i]+1)) 40 x-=(L)(n-sa[i]-height[i]+1); 41 else{ 42 ll=sa[i],rr=sa[i]+height[i]+k-1; 43 return; 44 } 45 } 46 }*/ 47 inline bool cmp(int l,int r){ 48 for(int i=l,j=ll;i<=r&&j<=rr;++i,++j){ 49 if(s[i]<s[j]) 50 return true; 51 if(s[i]>s[j]) 52 return false; 53 } 54 // cout<<l<<' '<<r<<' '<<ll<<' '<<rr<<endl; 55 if(rr-ll<r-l) 56 return false; 57 return true; 58 } 59 inline bool check(){ 60 int i,j,cnt(0); 61 for(i=n;i>=1;i=j,++cnt){ 62 for(j=i;j>0;--j) 63 if(!cmp(j,i)) 64 break; 65 if(i==j) 66 return false; 67 } 68 return cnt<=k; 69 } 70 L sum[100005]; 71 int main(){ 72 scanf("%d%s",&k,s+1); 73 n=strlen(s+1); 74 m=130; 75 Suffix(); 76 tot=n; 77 for(int i=1;i<=n;++i){ 78 sum[i]=n-sa[i]+1-height[i]; 79 sum[i]+=sum[i-1]; 80 tot+=n-sa[i]-height[i]; 81 } 82 L l(0),r(tot); 83 while(l<=r){ 84 L mid((l+r)>>1); 85 // cout<<l<<' '<<r<<" "<<mid<<endl; 86 // get_kth(mid); 87 L tmp(lower_bound(sum+1,sum+n+1,mid)-sum); 88 ll=sa[tmp],rr=mid-sum[tmp-1]+sa[tmp]+height[tmp]-1; 89 // cout<<ll<<' '<<rr<<' '<<mid<<endl; 90 if(check()){ 91 ansl=ll,ansr=rr; 92 r=mid-1; 93 } 94 else 95 l=mid+1; 96 } 97 // cout<<ansl<<' '<<ansr<<endl; 98 for(int i=ansl;i<=ansr;++i) 99 putchar(s[i]); 100 }