bzoj 3230 相似子串——后缀数组

Posted narh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

 

以上是关于bzoj 3230 相似子串——后缀数组的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 3230 相似子串 | 后缀数组 二分 ST表

[BZOJ3230] 相似字串 后缀数组+RMQ

bzoj3230 相似子串

3230: 相似子串

bzoj 3230: 相似子串SA+st表+二分

[BZOJ3230]相似子串