• bzoj 3230 相似子串 —— 后缀数组+二分


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

    先算出每个后缀贡献子串的区间;

    然后前缀LCP直接查询,后缀LCP二分长度,查询即可;

    注意本质不同的子串的个数是 long long 级别!!

    于是读入有 long long —— 快读也要写成 long long 的!!!

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const xn=1e5+5;
    int n,m,sa[xn],rk[xn],tax[xn],tp[xn],ht[xn][20],bin[20],bit[xn];
    ll pl[xn],pr[xn],cnt;//ll!
    char s[xn];
    ll rd()//ll
    {
      ll 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;
    }
    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<=xn;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++)
        {
          ll num=n-sa[i]+1-ht[i][0];
          pl[i]=cnt+1; cnt+=num; pr[i]=cnt;
        }
    }
    void find(ll x,int &sl,int &sr)
    {
      int l=1,r=n,res;
      while(l<=r)
        {
          int mid=((l+r)>>1);
          if(x>=pl[mid]&&x<=pr[mid]){res=mid; sl=sa[mid]; break;}//mid:rk
          if(x<pl[mid])r=mid-1;
          else l=mid+1;
        }
      int st=sl+ht[res][0];
      sr=st+(x-pl[res]+1)-1;
    }
    int ask(int x,int y)
    {
      int l=0,r=min(x,y),res;//min
      while(l<=r)
        {
          int mid=((l+r)>>1);
          if(getlcp(x-mid+1,y-mid+1)>=mid)res=mid,l=mid+1;
          else r=mid-1;
        }
      return res;
    }
    int main()
    {
      n=rd(); int Q=rd();
      scanf("%s",s+1); m=125;
      work(); get(); init();
      for(int i=1;i<=Q;i++)
        {
          ll x=rd(),y=rd();//ll
          if(x>cnt||y>cnt){puts("-1"); continue;}
          int l1,r1,l2,r2; find(x,l1,r1); find(y,l2,r2);
          int mnn=min(r1-l1+1,r2-l2+1);
          int a=min(getlcp(l1,l2),mnn);
          int b=min(ask(r1,r2),mnn);
          printf("%lld
    ",(ll)a*a+(ll)b*b);
        }
      return 0;
    }
  • 相关阅读:
    分布式系统的负载均衡以及ngnix负载均衡的五种策略
    排序
    servlet+forward和direct区别
    https、socket、http协议
    类加载机制+变量初始化
    MySQL中的索引
    线程状态
    JVM的分区+查看GC对象是否存活+3种GC算法+7种垃圾收集器+如何减少GC次数
    ORACLE索引监控的简单使用
    如何验证所做的AIX系统备份是否可用
  • 原文地址:https://www.cnblogs.com/Zinn/p/10084000.html
Copyright © 2020-2023  润新知