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;
    }
  • 相关阅读:
    Eclipse 导入项目乱码问题(中文乱码)
    sql中视图视图的作用
    Java基础-super关键字与this关键字
    Android LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot)的参数理解
    Android View和ViewGroup
    工厂方法模式(java 设计模式)
    设计模式(java) 单例模式 单例类
    eclipse乱码解决方法
    No resource found that matches the given name 'Theme.AppCompat.Light 的完美解决方案
    【转】使用 Eclipse 调试 Java 程序的 10 个技巧
  • 原文地址:https://www.cnblogs.com/Narh/p/10085056.html
Copyright © 2011-2022 走看看