后缀数组+二分
1.
从后往前贪心扫
当必须分的时候就分一段
注意比较两个串大小时的细节
2.
先二分这个串第一次出现时在后缀数组上的位置
再二分具体是哪个串
从前往后扫,使右端点不断往左缩,当 右<=左 时分一段
快的飞起
1.
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 200005 #define ll long long using namespace std; char s[N]; int rk[N],sa[N],wb[N],sum[N]; void sasa(int n,int m) { int *x=rk,*y=wb; for(int i=0;i<m;i++)sum[i]=0; for(int i=0;i<n;i++)sum[x[i]=s[i]]++; for(int i=1;i<m;i++)sum[i]+=sum[i-1]; for(int i=n-1;i>=0;i--)sa[--sum[x[i]]]=i; for(int j=1,p=1;p<n;m=p,j<<=1) { p=0; for(int i=n-j;i<n;i++)y[p++]=i; for(int i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j; for(int i=0;i<m;i++)sum[i]=0; for(int i=0;i<n;i++)sum[x[i]]++; for(int i=1;i<m;i++)sum[i]+=sum[i-1]; for(int i=n-1;i>0;i--)sa[--sum[x[y[i]]]]=y[i]; swap(x,y);x[sa[0]]=0;p=1; for(int i=1;i<n;i++) x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]?p-1:p++; } return ; } int h[N]; void calh(int n) { for(int i=1;i<=n;i++)rk[sa[i]]=i; int k=0; for(int i=0;i<n;i++) { if(k)k--; int j=sa[rk[i]-1]; while(s[j+k]==s[i+k])k++; h[rk[i]]=k; } return ; } int mn[N][20],lg[N],n; void ST() { lg[0]=-1; for(int i=1;i<=100000;i++)lg[i]=lg[i>>1]+1; for(int i=1;i<=n;i++)mn[i][0]=h[i]; for(int i=1;i<20;i++) { for(int j=1;j<=n;j++) { if(j+(1<<(i-1))<=n) { mn[j][i]=min(mn[j][i-1],mn[j+(1<<(i-1))][i-1]); } else mn[j][i]=mn[j][i-1]; } }return ; } int qur(int l,int r) { int k=lg[r-l+1]; return min(mn[l][k],mn[r-(1<<k)+1][k]); } bool cmp(int x,int len1,int y,int len2) { if(x<y) { int p=qur(x+1,y); if(p>=len2&&p<len1)return 0; return 1; } else if(x>y) { int p=qur(y+1,x); if(p>=len1)return 1; return 0; } else { if(len1<=len2)return 1; return 0; } } int k; bool pan(ll x) { int now=1;ll tot=0; int pos,len; for(int i=1;i<=n;i++) { ll pre=tot; tot+=n-sa[i]-h[i]; if(tot>=x) { pos=i;len=h[i]+x-pre; break; } } int ta=n-1; for(int i=n-1;i>=0;i--) { if(!cmp(rk[i],1,pos,len))return 0; if(!cmp(rk[i],ta-i+1,pos,len)) { ta=i,now++; } } return now<=k; } int main() { scanf("%d",&k); scanf("%s",s); n=strlen(s); sasa(n+1,256); calh(n); ST(); ll sm=1LL*n*(n+1)/2; for(int i=1;i<=n;i++)sm-=h[i]; ll l=1,r=sm; while(l<r) { ll mid=(l+r)>>1; if(pan(mid))r=mid; else l=mid+1; } ll tot=0; int pos,len; for(int i=1;i<=n;i++) { ll pre=tot; tot+=n-sa[i]-h[i]; if(tot>=l) { pos=i;len=h[i]+l-pre; break; } } for(int i=0;i<len;i++) { putchar(s[sa[pos]+i]); }puts(""); return 0; }
2.
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 200005 using namespace std; char s[N]; int c[N],sa[N],m,n,rk[N],t1[N],t2[N],v[N]; void saa(char *s,int n,int m) { int *x=t1,*y=t2; for(int i=0;i<m;i++)c[i]=0; for(int i=0;i<n;i++)c[x[i]=s[i]]++; for(int i=1;i<m;i++)c[i]+=c[i-1]; for(int i=0;i<n;i++)sa[--c[x[i]]]=i; int p=1; for(int j=1;p<n;j<<=1,m=p) { p=0; for(int i=n-j;i<n;i++)y[p++]=i; for(int i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j; for(int j=0;j<m;j++)c[j]=0; for(int i=0;i<n;i++)c[x[i]]++; for(int j=1;j<m;j++)c[j]+=c[j-1]; for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; swap(x,y);x[sa[0]]=0;p=1; for(int i=1;i<n;i++) x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]?p-1:p++; } return ; } int h[N]; void calh(int n) { for(int i=1;i<=n;i++)rk[sa[i]]=i; int k=0; for(int i=0;i<n;i++) { if(k)k--; int j=sa[rk[i]-1]; while(s[i+k]==s[j+k])k++; h[rk[i]]=k; } return ; } int main() { scanf("%d %s",&m,s); n=strlen(s);s[n]=0; saa(s,n+1,300); calh(n); int l=1,r=n; while(l<r) { int mid=(l+r)>>1,num=0,mn=n,m1=n; for(int i=mid+1;i<=n;i++){m1=min(m1,h[i]);v[sa[i]]=m1;if(!h[i])num=m+1;} for(int i=0;i<=n-1;i++) { if(mn==i)num++,mn=n; if(v[i])mn=min(mn,i+v[i]); } if(num+1<=m)r=mid;else l=mid+1; for(int i=mid+1;i<=n;i++)v[sa[i]]=0; } int u=l; l=h[u]+1,r=n-sa[u]; while(l<r) { int mid=(l+r)>>1,k=u,num=0,mn=n,m1=mid; for(int i=u+1;i<=n;i++){m1=min(m1,h[i]);v[sa[i]]=m1;} v[sa[u]]=mid; for(int i=0;i<n;i++) { if(mn==i)num++,mn=n; if(v[i])mn=min(mn,i+v[i]); } if(num+1<=m)r=mid;else l=mid+1; for(int i=k;i<=u;i++)v[sa[i]]=0; } for(int i=1;i<=l;i++)printf("%c",s[sa[u]+i-1]); puts(""); return 0; }