zoukankan      html  css  js  c++  java
  • bzoj 3230 相似子串——后缀数组

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

    作出后缀数组,从 LCP 看每个位置对于本质不同子串的贡献,而且他们已经按前面部分排好序了,所以直接在 sa[ ] 上二分就能找到询问的子串。

    找最长公共前缀就用 ht[ ] 和子串的长度比较就行。找最长公共后缀就一开始把原串翻转,做出翻转后的 ht[ ] ,就能查询了。

    也可以二分一个最长公共后缀的位置,然后用正常的 ht[ ] 判断。

    注意 long long 。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=1e5+5,K=20;
    int n,sa[2][N],rk[2][N],tp[N],tx[N];ll sm[N];
    int ht[2][N][K],bin[K],lg[N];
    char s[N];
    struct Node{int ps,len;};
    int Mn(int a,int b){return a<b?a:b;}
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    ll rdl()
    {
      ll ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    void init()
    {
      lg[1]=0;for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
      bin[0]=1;for(int i=1;i<=lg[n];i++)bin[i]=bin[i-1]<<1;
    }
    void Rsort(int n,int nm,int x)
    {
      for(int i=1;i<=nm;i++)tx[i]=0;
      for(int i=1;i<=n;i++)tx[rk[x][i]]++;
      for(int i=2;i<=nm;i++)tx[i]+=tx[i-1];
      for(int i=n;i;i--)sa[x][tx[rk[x][tp[i]]]--]=tp[i];
    }
    void get_sa(int n,int x)
    {
      int nm=30;
      for(int i=1;i<=n;i++)tp[i]=i,rk[x][i]=s[i]-'a'+1;
      Rsort(n,nm,x);
      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[x][i]>k)tp[++tot]=sa[x][i]-k;
          Rsort(n,nm,x);memcpy(tp,rk[x],sizeof rk[x]);
          nm=1;rk[x][sa[x][1]]=1;
          for(int i=2,u,v;i<=n;i++)
        {
          u=sa[x][i]+k;v=sa[x][i-1]+k;if(u>n)u=0;if(v>n)v=0;
          rk[x][sa[x][i]]=(tp[sa[x][i]]==tp[sa[x][i-1]]&&tp[u]==tp[v])?nm:++nm;
        }
          if(nm==n)break;
        }
    }
    void get_ht(int n,int x)
    {
      for(int i=1,k=0,j;i<=n;i++)
        {
          for(k?k--:0,j=sa[x][rk[x][i]-1];i+k<=n&&j+k<=n&&s[i+k]==s[j+k];k++);
          ht[x][rk[x][i]][0]=k;
        }
      for(int j=1;j<=lg[n];j++)
        for(int i=1;i+bin[j]-1<=n;i++)
          ht[x][i][j]=Mn(ht[x][i][j-1],ht[x][i+bin[j-1]][j-1]);
    }
    int get_lcp(int l,int r,int x)
    {
      if(l==r)return n-l+1;
      l=rk[x][l]; r=rk[x][r]; if(l>r)swap(l,r);
      int d=lg[r-l];
      return Mn(ht[x][l+1][d],ht[x][r-bin[d]+1][d]);
    }
    Node fnd(ll x)
    {
      int l=1,r=n,ret=0;
      while(l<=r)
        {
          int mid=l+r>>1;
          if(sm[mid]>=x)ret=mid,r=mid-1;
          else l=mid+1;
        }
      if(!ret)return (Node){0,0};
      int d=n-sa[0][ret]+1-ht[0][ret][0];
      d=ht[0][ret][0]+(x-sm[ret-1]);
      return (Node){sa[0][ret],d};
    }
    int main()
    {
      n=rdn();int Q=rdn();scanf("%s",s+1);init();
      get_sa(n,0);get_ht(n,0);
      reverse(s+1,s+n+1);get_sa(n,1);get_ht(n,1);
      for(int i=1;i<=n;i++)
        sm[i]=sm[i-1]+(n-sa[0][i]+1)-ht[0][i][0];
      ll x,y; Node a,b;//long long!!!
      while(Q--)
        {
          x=rdl(); y=rdl();//long long!!
          a=fnd(x); b=fnd(y);
          if(!a.ps||!b.ps){puts("-1");continue;}
          x=Mn(get_lcp(a.ps,b.ps,0),Mn(a.len,b.len));
          y=Mn(get_lcp(n-(a.ps+a.len-1)+1,n-(b.ps+b.len-1)+1,1),Mn(a.len,b.len));
          printf("%lld
    ",x*x+y*y);
        }
      return 0;
    }
  • 相关阅读:
    Java虚拟机--编译那点事儿
    Java虚拟机--常用Java命令(二)
    Java虚拟机--常用Java命令(一)
    Java虚拟机--类加载机制
    Java虚拟机--JIT编译器
    Java虚拟机--垃圾回收机制
    Java虚拟机--对象模型
    Junit测试--多个测试接口如何产生业务联系
    Java虚拟机--内存模型
    Java虚拟机--内存结构
  • 原文地址:https://www.cnblogs.com/Narh/p/10084051.html
Copyright © 2011-2022 走看看