zoukankan      html  css  js  c++  java
  • bzoj 4556 字符串 —— 后缀数组+主席树

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

    就是找一个 rk 在一段区间内的前驱和后继;

    由于 LCP 还有区间长度的限制,所以可以先二分答案!

    然后直接建立 rk 的主席树,查询即可。

    代码如下:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define mid ((l+r)>>1)
    using namespace std;
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    int Min(int x,int y){return x<y?x:y;}
    int Max(int x,int y){return x>y?x:y;}
    int const xn=1e5+5,xm=1e5*18;
    int n,m,rk[xn],tp[xn],sa[xn],tax[xn],ht[xn][20],bin[20],bit[xn];
    int cnt,rt[xn],ls[xm],rs[xm],sum[xm];
    char s[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 init()
    {
      for(int i=1;i<=n;i++)tp[i]=i,rk[i]=s[i];
      m=125; 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 geth()
    {
      int k=0;
      for(int i=1;i<=n;i++)
        {
          if(rk[i]==1)continue;//continue
          if(k)k--; int j=sa[rk[i]-1];
          while(s[i+k]==s[j+k]&&i+k<=n&&j+k<=n)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 i=1;i<20;i++)
        for(int j=1;j<=n&&j+bin[i]-1<=n;j++)
          ht[j][i]=Min(ht[j][i-1],ht[j+bin[i-1]][i-1]);
    }
    int getlcp(int x,int y)//rk
    {
      if(x==y)return n-sa[x]+1;
      if(x>y)swap(x,y); x++;
      int r=bit[y-x+1];
      return Min(ht[x][r],ht[y-bin[r]+1][r]);
    }
    void build(int &x,int y,int l,int r,int v)
    {
      x=++cnt; ls[x]=ls[y]; rs[x]=rs[y]; sum[x]=sum[y]+1;
      if(l==r)return;
      if(v<=mid)build(ls[x],ls[y],l,mid,v);
      else build(rs[x],rs[y],mid+1,r,v);
    }
    int findl(int x,int y,int l,int r)
    {
      if(!(sum[x]-sum[y]))return -1;
      if(l==r)return l;
      if(sum[ls[x]]-sum[ls[y]])return findl(ls[x],ls[y],l,mid);
      return findl(rs[x],rs[y],mid+1,r);
    }
    int findr(int x,int y,int l,int r)
    {
      if(!(sum[x]-sum[y]))return -1;
      if(l==r)return l;
      if(sum[rs[x]]-sum[rs[y]])return findr(rs[x],rs[y],mid+1,r);
      return findr(ls[x],ls[y],l,mid);
    }
    int queryp(int x,int y,int l,int r,int v)
    {
      if(!(sum[x]-sum[y]))return -1;
      if(l==r)return l;
      if(v<=mid)return queryp(ls[x],ls[y],l,mid,v);
      else
        {
          int tmp=queryp(rs[x],rs[y],mid+1,r,v);
          if(tmp!=-1)return tmp;
          return findr(ls[x],ls[y],l,mid);
        }
    }
    int querys(int x,int y,int l,int r,int v)
    {
      if(!(sum[x]-sum[y]))return -1;
      if(l==r)return l;
      if(v>mid)return querys(rs[x],rs[y],mid+1,r,v);
      else
        {
          int tmp=querys(ls[x],ls[y],l,mid,v);
          if(tmp!=-1)return tmp;
          return findl(rs[x],rs[y],mid+1,r);
        }
    }
    bool ck(int a,int b,int x,int v)
    {
      int L=a,R=b-x+1;
      int pr=queryp(rt[R],rt[L-1],1,n,v),sc=querys(rt[R],rt[L-1],1,n,v);
      int len=0;
      if(pr!=-1)len=Max(len,getlcp(pr,v));
      if(sc!=-1)len=Max(len,getlcp(sc,v));
      return len>=x;
    }
    int main()
    {
      n=rd(); int Q=rd();
      scanf("%s",s+1); init(); geth();
      for(int i=1;i<=n;i++)build(rt[i],rt[i-1],1,n,rk[i]);
      for(int i=1,a,b,c,d;i<=Q;i++)
        {
          a=rd(); b=rd(); c=rd(); d=rd();
          int l=0,r=Min(b-a+1,d-c+1),res;
          while(l<=r)
        {
          if(ck(a,b,mid,rk[c]))res=mid,l=mid+1;
          else r=mid-1;
        }
          printf("%d
    ",res);
        }
      return 0;
    }
  • 相关阅读:
    阿里消息队列中间件 RocketMQ 源码分析 —— Message 拉取与消费(上)
    数据库中间件 ShardingJDBC 源码分析 —— SQL 解析(三)之查询SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(六)之删除SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(五)之更新SQL
    消息队列中间件 RocketMQ 源码分析 —— Message 存储
    源码圈 300 胖友的书单整理
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(一)分库分表配置
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(四)之插入SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(二)之分库分表路由
    C#中Math类的用法
  • 原文地址:https://www.cnblogs.com/Zinn/p/10317432.html
Copyright © 2011-2022 走看看