zoukankan      html  css  js  c++  java
  • bzoj 4310 跳蚤 —— 后缀数组+二分答案+贪心

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4310

    二分答案——在本质不同的子串中二分答案!

    如果二分到的子串位置是 st,考虑何时必须分出一段;

    如果一个位置 i 有 rk[i] < rk[st],那么显然不用管( i 后缀的开头);

    而如果 rk[i] > rk[st],则 i+1 ~ i+LCP 之间必须有一处断开;

    比较麻烦的是这样只保证了 i 这个后缀的开头不会产生大于二分子串的子串,但后面怎么办?

    这时就又需要考虑到——后缀的后缀也是后缀!所以倒着做,就不用考虑开头后面的问题了!

    然后如果必须断开,断开处贪心地靠前即可。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const xn=1e5+5;
    int n,m,K,rk[xn],sa[xn],tp[xn],tax[xn],ht[xn][20],bin[20],bit[xn],dc[xn];
    ll sum[xn];
    char s[xn],ch[xn];
    void Rsort()
    {
      for(int i=1;i<=m;i++)tax[i]=0;
      for(int i=1;i<=n;i++)tax[rk[tp[i]]]++;
      for(int i=1;i<=m;i++)tax[i]+=tax[i-1];
      for(int i=n;i;i--)sa[tax[rk[tp[i]]]--]=tp[i];
    }
    void work()
    {
      for(int i=1;i<=n;i++)rk[i]=s[i],tp[i]=i;
      Rsort();
      for(int k=1;k<=n;k<<=1)
        {
          int num=0;
          for(int i=n-k+1;i<=n;i++)tp[++num]=i;
          for(int i=1;i<=n;i++)
        if(sa[i]>k)tp[++num]=sa[i]-k;
          Rsort(); swap(rk,tp);
          rk[sa[1]]=1; num=1;
          for(int i=2;i<=n;i++)
        rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])?num:++num;
          if(num==n)break;
          m=num;
        }
    }
    void get()
    {
      int k=0;
      for(int i=1;i<=n;i++)
        {
          if(rk[i]==1)continue;
          if(k)k--; int j=sa[rk[i]-1];
          while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
          ht[rk[i]][0]=k;
        }
      bin[0]=1; for(int i=1;i<20;i++)bin[i]=(bin[i-1]<<1);
      bit[1]=0; for(int i=2;i<=n;i++)bit[i]=bit[i>>1]+1;
      for(int j=1;j<20;j++)
        for(int i=1;i<=n&&i+bin[j]-1<=n;i++)
          ht[i][j]=min(ht[i][j-1],ht[i+bin[j-1]][j-1]);
    }
    int getlcp(int x,int y)
    {
      if(x==y)return n-x+1;
      x=rk[x]; y=rk[y];
      if(x>y)swap(x,y); x++;
      int w=bit[y-x+1];
      return min(ht[x][w],ht[y-bin[w]+1][w]);
    }
    void init()
    {
      for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+n-sa[i]+1-ht[i][0];
    }
    void find(ll x,int &st,int &len)
    {
      int l=1,r=n,res;
      while(l<=r)
        {
          int mid=((l+r)>>1);
           if(x>sum[mid-1]&&x<=sum[mid]){res=mid; break;}//res:rk
          if(x<=sum[mid-1])r=mid-1;
          else l=mid+1;
        }
      st=sa[res];//
      len=ht[res][0]+x-sum[res-1];
    }
    int cal(int st,int len)
    {
      int lst=n+1,cnt=1;//1
      for(int i=n,lcp;i;i--)
        {
          if(rk[i]<rk[st])continue;
          if((lcp=getlcp(i,st))==0)return K+1;
          lcp=min(lcp,len);//
          if(lst>i&&lst<=i+lcp)continue;
          lst=i+1; cnt++;
        }
      return cnt;
    }
    int main()
    {
      scanf("%d",&K); scanf("%s",s+1); n=strlen(s+1);
      for(int i=1;i<=n;i++)dc[i]=(int)s[i],ch[i]=s[i];
      sort(dc+1,dc+n+1); m=unique(dc+1,dc+n+1)-dc-1;
      for(int i=1;i<=n;i++)s[i]=lower_bound(dc+1,dc+m+1,(int)s[i])-dc;
      work(); get(); init();
      ll l=1,r=sum[n]; int pos=0,ans=0;
      while(l<=r)
        {
          ll mid=((l+r)>>1ll);
          int st,len; find(mid,st,len);
          if(cal(st,len)<=K)pos=st,ans=len,r=mid-1;
          else l=mid+1;
        }
      for(int i=pos;i<pos+ans;i++)putchar(ch[i]); puts("");
      return 0;
    }
  • 相关阅读:
    hibernate持久化框架
    spring之AOP
    spring之bean
    spring之IOC
    pdf文件工具typora
    vsCode写vue项目一键生成.vue模板
    微信小程序瀑布流
    小程序接入阿拉丁
    小程序引入背景图片不显示问题解决
    Mac OS下使用rz和sz
  • 原文地址:https://www.cnblogs.com/Zinn/p/10085037.html
Copyright © 2011-2022 走看看