【bzoj4310】跳蚤
-
题解:
- 读了半个小时题。。。首先明确题意:求$S$分成至多$k$个串,每个串的子串的最大字典序的最大字典序(要选两次最大)最小
- 求出sa和ht,本质不同的子串的个数等于总个数减去$sa$里面前后相同的个数:$sum = \sum len - sa[i] - ht[i] $
- 因为在后缀数组中本质相同的子串一定出现在$sa$的连续一段的$lcp$里;
- 二分本质不同的串为第mid个,可以利用上面的结论$O(n)$求出第mid个串是什么;
- 从后往前贪心,能不放就不放,比较两个串可以先求$lcp$再$O(1)$比较
- 所以是$O(N \ logN)$的
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define Run(i,l,r) for(int i=l;i<=r;i++) 5 #define Don(i,l,r) for(int i=l;i>=r;i--) 6 using namespace std; 7 typedef long long ll; 8 const int N=100010; 9 int n,k,m,sa[N],ht[N],rk[N],c[N],x[N],y[N],b[N],f[N][20],lg[N],ansl,ansr; 10 char s[N]; 11 void build_sa(){ 12 m=128; 13 for(int i=0;i<m;i++) c[i]=0; 14 for(int i=0;i<n;i++) c[x[i]=s[i]]++; 15 for(int i=1;i<m;i++) c[i]+=c[i-1]; 16 for(int i=n-1;~i;i--) sa[--c[x[i]]]=i; 17 int p; 18 //for(int i=0;i<n;i++) cout<<sa[i]<<endl;cout<<endl; 19 for(int k=1;k<n;k<<=1){ 20 p=0; 21 for(int i=n-k;i<n;i++) y[p++]=i; 22 for(int i=0;i<n;i++) if(sa[i]>=k)y[p++]=sa[i]-k; 23 for(int i=0;i<m;i++) c[i]=0; 24 for(int i=0;i<n;i++) c[x[y[i]]]++; 25 for(int i=1;i<m;i++) c[i]+=c[i-1]; 26 for(int i=n-1;~i;i--) sa[--c[x[y[i]]]]=y[i]; 27 swap(x,y); p=1;x[sa[0]]=0; 28 for(int i=1;i<n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p-1:p++; 29 if(p>=n) break; 30 m=p+1; 31 // for(int i=0;i<n;i++) cout<<sa[i]<<endl;cout<<endl; 32 } 33 }//////////////// 34 void build_ht(){ 35 for(int i=0;i<n;i++)rk[sa[i]]=i; 36 for(int i=0,k=0,j;i<n;ht[rk[i++]]=k) 37 for(k?k--:0,j=sa[rk[i]-1];rk[i]&&s[i+k]==s[j+k];k++); 38 }///// 39 void rmq(){ 40 lg[1]=0;for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1; 41 for(int i=0;i<=n;i++) f[i][0]=ht[i]; 42 for(int i=1;i<17;i++) 43 for(int j=0;j+(1<<i)<=n;j++) 44 f[j][i]=min(f[j][i-1],f[j+(1<<i-1)][i-1]); 45 }//// 46 int lcp(int x,int y){ 47 if(x==y)return n-x;//// 48 x=rk[x],y=rk[y]; 49 if(x>y) swap(x,y); 50 int t=lg[y-x]; 51 int tmp; 52 tmp=min(f[x+1][t],f[y-(1<<t)+1][t]);/// 53 // printf("%d %d %d\n",x,y,tmp); 54 return tmp; 55 }///// 56 int l,r,len; 57 void find(ll k){ 58 int i; 59 for(i=1;i<=n;k-=b[i++])//// 60 if(b[i]>=k){l=sa[i],r=sa[i]+ht[i]+k-1;len=r-l+1;break;}/// 61 //printf("%d\n",i); 62 }///// 63 bool ask(int L,int R){ 64 int Len=R-L+1; 65 int t=min(min(len,Len),lcp(l,L)); 66 if(t==Len&&t<=len) return 1; 67 if(t==len) return 0; 68 return s[l+t]>s[L+t];//// 69 }//// 70 bool check(){ 71 int i,j,cnt=0; 72 for(i=j=n-1;~i;i=j,cnt++){ 73 while(~j&&ask(j,i))j--; 74 if(j==i||cnt>k)return 0; 75 // printf("%d\n",j); 76 } 77 // printf("\n"); 78 return cnt<=k; 79 }///// 80 int main() 81 { //freopen("bzoj4310.in","r",stdin); 82 //freopen("bzoj4310.out","w",stdout); 83 scanf("%d",&k); 84 scanf("%s",s); n=strlen(s); s[n++]=0; 85 build_sa(); build_ht(); rmq();// 86 ll L=1,R=0,mid; n--; 87 for(int i=1;i<=n;i++) R+=(b[i]=n-sa[i]-ht[i]);/// 88 while(L<=R){ 89 find(mid=L+R>>1); 90 if(check())ansl=l,ansr=r,R=mid-1;else L=mid+1;// 91 } 92 for(int i=ansl;i<=ansr;i++)putchar(s[i]);// 93 return 0; 94 }//by tkys_Austin;