BZOJ 4310 二分+SA+RMQ
思路:
首先求出后缀数组和height数组,这样能得到本质不同的子串数目
这里利用:本质不同的子串=∑(Len−SA[i]−height[i])=∑(Len−SA[i]−height[i])利用SA[],height[]的定义很好想
然后要求最大值最小,显然二分,二分一个mid,求出第mid大的子串
然后贪心的检验,从后往前扫,当字典序超过二分的值时,划分一下,看划分个数与K的关系即可
中间涉及比较,用LCP实现即可,显然ST表非常方便
From Dad3zZ
//By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=100050; typedef long long ll; int n,k,cntA[N],cntB[N],A[N],B[N],sa[N],tsa[N],rk[N],ht[N],Log[N],st[N][20]; int L,R;ll l,r,ans; char s[N]; void SA(){ for(int i=1;i<=n;i++)cntA[s[i]]++; for(int i=1;i<=256;i++)cntA[i]+=cntA[i-1]; for(int i=n;i;i--)sa[cntA[s[i]]--]=i; rk[sa[1]]=1; for(int i=2;i<=n;i++)rk[sa[i]]=rk[sa[i-1]]+(s[sa[i]]!=s[sa[i-1]]); for(int l=1;rk[sa[n]]<n;l<<=1){ memset(cntA,0,sizeof(cntA)); memset(cntB,0,sizeof(cntB)); for(int i=1;i<=n;i++) cntA[A[i]=rk[i]]++, cntB[B[i]=i+l<=n?rk[i+l]:0]++; for(int i=1;i<=n;i++)cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1]; for(int i=n;i;i--)tsa[cntB[B[i]]--]=i; for(int i=n;i;i--)sa[cntA[A[tsa[i]]]--]=tsa[i]; rk[sa[1]]=1; for(int i=2;i<=n;i++)rk[sa[i]]=rk[sa[i-1]]+(A[sa[i]]!=A[sa[i-1]]||B[sa[i]]!=B[sa[i-1]]); } for(int i=1,j=0;i<=n;i++){ j=j?j-1:0,Log[i]=i!=1?Log[i>>1]+1:0; while(s[i+j]==s[sa[rk[i]-1]+j])j++; st[rk[i]][0]=ht[rk[i]]=j; } for(int j=1;j<=19;j++)for(int i=1;i+(1<<(j-1))<=n;i++)st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } int lcp(int x,int y){ if(x==y)return n-x+1; x=rk[x],y=rk[y]; if(x>y)swap(x,y);x++; int t=Log[y-x+1]; return min(st[x][t],st[y-(1<<t)+1][t]); } void get(ll k){ for(int i=1;i<=n;i++){ if(n-sa[i]-ht[i]+1<k)k=k-(n-sa[i]-ht[i]+1); else{L=sa[i],R=sa[i]+ht[i]+k-1;break;} } } bool cmp(int l1,int r1,int l2,int r2){ int len1=r1-l1+1,len2=r2-l2+1,LCP=lcp(l1,l2); if(len1<=len2&&LCP>=len1)return 1; if(len1>len2&&LCP>=len2)return 0; if(LCP>=len1&&LCP>=len2)return len1<len2; return s[l1+LCP]<s[l2+LCP]; } bool check(){ int cnt=1,last=n; for(int i=n;i;i--){ if(!cmp(i,last,L,R))cnt++,last=i; if(cnt>k)return 0; }return 1; } int main(){ scanf("%d%s",&k,s+1),n=strlen(s+1),SA(); for(int i=1;i<=n;i++)r+=n-sa[i]-ht[i]+1; while(l<=r){ ll mid=(l+r)>>1; get(mid); if(check())r=mid-1,ans=mid; else l=mid+1; }get(ans); s[R+1]=0;printf("%s\n",s+L); }