zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    AGC003E
    Vegetable's Refrain
    error C4996: 'stricmp': The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name: _stricmp.
    给linux shell 添加ll命令支持
    编译WDF驱动项目时,缺少WDKConversionPreConfiguration.props文件的问题
    idea报错:Please, configure Web Facet first!以及打开网页后出现:HTTP状态 404
    php安装gd库
    Shell下实现免密码快速登陆MySQL数据库的方法
    mysql命令导入百万数据
    java 两个数字相除后保留小数点
  • 原文地址:https://www.cnblogs.com/Narh/p/10085056.html
Copyright © 2011-2022 走看看