bzoj 4310 跳蚤——后缀数组+二分答案+贪心
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4310
答案有单调性?
二分出来一个子串,判断的时候需要满足那些字典序比它大的子串都不出现!
原来想的是在 sa[ ] 上二分找到最右端 j ,满足自己到 j 之间的位置与自己的 LCP >= ans_len ;然后从前往后遍历,如果走到一个位置 k 发现它的 sa[ ] 是在那个 LCP >= ans_len 的区间内的,则需要把它截断;可以在 k ~ k+ans_len-1 之间选一个位置(在该位置后面截断);如果这段区间里没有之前弄出来的截断的话,就贪心地在最靠后放一个。
但这样不能让所有字典序比自己大的子串都不出现。
看看题解,原来是从后往前,一边通过 rk[ ] 来判断这个位置需不需要截断。要截断的话,范围就是当前位置 k 到 k + min( LCP , ans_len ) ;贪心就是如果还没被截断的话就在 k+1 位置截开。注意 LCP == 0 的话这个二分值一定不是答案,因为长度为1的子串不能被截开了。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=1e5+5,K=20; int n,a[N],sa[N],rk[N],tp[N],tx[N],ht[N][K],lg[N],bin[K]; ll sm[N];char s[N]; int Mn(int a,int b){return a<b?a:b;} void Rsort(int n,int nm) { for(int i=1;i<=nm;i++)tx[i]=0; for(int i=1;i<=n;i++)tx[rk[i]]++; for(int i=2;i<=nm;i++)tx[i]+=tx[i-1]; for(int i=n;i;i--)sa[tx[rk[tp[i]]]--]=tp[i]; } void get_sa(int n,int nm) { for(int i=1;i<=n;i++)tp[i]=i,rk[i]=a[i]; Rsort(n,nm); for(int k=1;k<=n;k<<=1) { int tot=0; for(int i=n-k+1;i<=n;i++)tp[++tot]=i; for(int i=1;i<=n;i++) if(sa[i]>k)tp[++tot]=sa[i]-k; Rsort(n,nm);memcpy(tp,rk,sizeof rk);nm=1;rk[sa[1]]=1; for(int i=2,u,v;i<=n;i++) { u=sa[i]+k;v=sa[i-1]+k;if(u>n)u=0;if(v>n)v=0; rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[u]==tp[v])?nm:++nm; } if(nm==n)break; } } void get_ht(int n) { lg[1]=0;for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;//i=2 bin[0]=1;for(int i=1;i<=lg[n];i++)bin[i]=bin[i-1]<<1; for(int i=1,k=0,j;i<=n;i++) { for(k?k--:0,j=sa[rk[i]-1];i+k<=n&&j+k<=n&&s[i+k]==s[j+k];k++); ht[rk[i]][0]=k; } for(int j=1;j<=lg[n];j++) for(int i=1;i+bin[j]-1<=n;i++) ht[i][j]=Mn(ht[i][j-1],ht[i+bin[j-1]][j-1]); } void fnd(ll x,int &id,int &len) { int l=1,r=n; while(l<=r) { int mid=l+r>>1; if(sm[mid]>=x)id=mid,r=mid-1; else l=mid+1; } len=n-sa[id]+1-(sm[id]-x); } int get_lcp(int l,int r) { if(l==r)return n-l+1; l=rk[l]; r=rk[r]; if(l>r)swap(l,r); int d=lg[r-l]; return Mn(ht[l+1][d],ht[r-bin[d]+1][d]);//l+1 } int chk(int id,int len) { int cnt=0,lst=n+1; for(int i=n;i;i--) { if(rk[i]<id)continue; int d=Mn(get_lcp(i,sa[id]),len);//Mn if(!d)return K; if(lst<=i+d)continue; cnt++; lst=i+1; } return cnt; } int main() { int lm;scanf("%d",&lm);lm--; scanf("%s",s+1);n=strlen(s+1); for(int i=1;i<=n;i++)tp[i]=(int)s[i]; sort(tp+1,tp+n+1); int m=unique(tp+1,tp+n+1)-tp-1; for(int i=1;i<=n;i++)a[i]=lower_bound(tp+1,tp+m+1,(int)s[i])-tp; get_sa(n,m); get_ht(n); for(int i=1;i<=n;i++)sm[i]=sm[i-1]+(n-sa[i]+1)-ht[i][0]; ll l=1,r=sm[n]; int id,len,pid,plen; while(l<=r) { ll mid=l+r>>1;fnd(mid,id,len); if(chk(id,len)<=lm)pid=id,plen=len,r=mid-1; else l=mid+1; } for(int i=sa[pid],j=1;j<=plen;i++,j++)putchar(s[i]);puts(""); return 0; }