BZOJ4310:跳蚤

4310: 跳蚤

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 543  Solved: 246
[Submit][Status][Discuss]

Description

很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。
首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k 个子串中选择字典序最大的那一个。他称其为“魔力串”。
现在他想找一个最优的分法让“魔力串”字典序最小。

Input

第一行一个整数 k。
接下来一个长度不超过 105 的字符串 S。

Output

输出一行,表示字典序最小的“魔力串”。

Sample Input

13
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 }

 

 

posted @ 2017-06-07 20:02  QYP_2002  阅读(664)  评论(0编辑  收藏  举报