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

 

posted on 2018-12-07 20:19  Narh  阅读(154)  评论(0编辑  收藏  举报

导航