BZOJ4310:跳蚤
4310: 跳蚤
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 543 Solved: 246
[Submit][Status][Discuss]
Description
很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。
首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k 个子串中选择字典序最大的那一个。他称其为“魔力串”。
现在他想找一个最优的分法让“魔力串”字典序最小。
Input
第一行一个整数 k。
接下来一个长度不超过 105 的字符串 S。
Output
输出一行,表示字典序最小的“魔力串”。
Sample Input
13
bcbcbacbbbbbabbacbcbacbbababaabbbaabacacbbbccaccbcaabcacbacbcabaacbccbbcbcbacccbcccbbcaacabacaaaaaba
bcbcbacbbbbbabbacbcbacbbababaabbbaabacacbbbccaccbcaabcacbacbcabaacbccbbcbcbacccbcccbbcaacabacaaaaaba
Sample Output
cbc
HINT
S的长度<=100000
思路{
欲求max(min),有二分答案的排名k,排名越大,越容易满足要求,越小则越难,满足单调性。
由之前套路可得:所有的本质不同子串个数为∑n-sa[i]-height[i]
可查找排名为k的串,然后从后面开始枚举子串,比较答案串和当前串,若当前串较大,是不能让他出现的,切一段,更新串的右端点。判断切多少次.基于此二分。
!:RMQ处理LCP细节!
}
1 #include<map> 2 #include<set> 3 #include<list> 4 #include<deque> 5 #include<cmath> 6 #include<queue> 7 #include<stack> 8 #include<vector> 9 #include<cstdio> 10 #include<complex> 11 #include<cstring> 12 #include<cstdlib> 13 #include<iostream> 14 #include<algorithm> 15 #define maxx 100010 16 #define RG register 17 #define LL long long 18 #define db double 19 using namespace std; 20 int sa[maxx],X[maxx],rnk[maxx],Y[maxx],height[maxx],dp[maxx][69],tong[maxx];char s[maxx]; 21 bool comp(int *r,int a,int b,int len){return r[a]==r[b]&&r[a+len]==r[b+len];} 22 void build_sa(int n){ 23 int *x=X,*y=Y,*t,Max=3000; 24 for(int i=0;i<=Max;++i)tong[i]=0; 25 for(int i=0;i<n;++i)tong[x[i]=s[i]]++; 26 for(int i=0;i<Max;++i)tong[i]+=tong[i-1]; 27 for(int i=n-1;i!=-1;i--)sa[--tong[x[i]]]=i; 28 for(int j=1,p=0,i;p<n;Max=p,j<<=1){ 29 for(i=n-1,p=0;i>=n-j;--i)y[p++]=i; 30 for(i=0;i<n;++i)if(sa[i]>=j)y[p++]=sa[i]-j; 31 for(i=0;i<=Max;++i)tong[i]=0; 32 for(i=0;i<n;++i)tong[x[y[i]]]++; 33 for(i=0;i<=Max;++i)tong[i]+=tong[i-1]; 34 for(i=n-1;i!=-1;i--)sa[--tong[x[y[i]]]]=y[i]; 35 for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;++i) 36 x[sa[i]]=comp(y,sa[i],sa[i-1],j)?p-1:p++; 37 } 38 }int n; 39 void geth(){ 40 int i,j,k=0; 41 for(i=1;i<=n;++i)rnk[sa[i]]=i; 42 for(i=0;i<n;height[rnk[i++]]=k) 43 for(k?k--:0,j=sa[rnk[i]-1];s[i+k]==s[j+k];k++); 44 } 45 void RMQ(){ 46 for(int i=1;i<=n;++i)dp[i][0]=height[i]; 47 for(int j=1;(1<<j)<=n;++j) 48 for(int i=1;i+(1<<(j-1))-1<=n;++i) 49 dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]); 50 } 51 LL LCP(int x,int y){ 52 if(x==y)return n-x+1; 53 x=rnk[x],y=rnk[y];if(x>y)swap(x,y);x++; 54 int t=(int)(log(double(y-x+1))/log(2.000)); 55 return min(dp[x][t],dp[y-(1<<t)+1][t]); 56 } 57 bool comp(int l,int r,int L,int R){//L,R 答案,l,r当前子串 取最小 58 int len=r-l+1,LEN=R-L+1,all=LCP(l,L); 59 if(all>=LEN&&LEN<len)return true; 60 if(all>=len&&LEN>=len)return false; 61 if(all>=len&&all>=LEN)return LEN<len; 62 return s[l+all]>s[L+all]; 63 }int L,R; 64 void gett(LL kk){//得到第k小串位置 65 LL sum=0; 66 for(int i=1;i<=n;++i){ 67 sum=n-sa[i]-height[i]; 68 if(sum<kk)kk-=sum; 69 else {L=sa[i],R=sa[i]+height[i]+kk-1;break;} 70 } 71 }int k; 72 bool check(){ 73 int to=n-1,cnt=1; 74 for(int i=n-1;i>=0;--i){ 75 if(s[i]>s[L])return false; 76 if(comp(i,to,L,R))cnt++,to=i; 77 if(cnt>k)return false; 78 }return true; 79 } 80 int main(){ 81 //freopen("1.in","r",stdin); 82 //freopen("1.out","w",stdout); 83 scanf("%d",&k); 84 scanf("%s",s);n=strlen(s),s[n]='0'; 85 build_sa(n+1),geth(),RMQ(); 86 LL l=1,r=0; 87 for(int i=1;i<=n;++i)r+=n-sa[i]-height[i]; 88 while(l<=r){ 89 LL mid=(l+r)>>1; 90 gett(mid); 91 if(check())r=mid-1; 92 else l=mid+1; 93 }gett(r+1);for(int i=L;i<=R;++i)cout<<s[i];puts(""); 94 return 0; 95 }