bzoj 2865 字符串识别——后缀数组

Posted narh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 2865 字符串识别——后缀数组相关的知识,希望对你有一定的参考价值。

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

做出 ht[ ] 之后,sa[ ] 上每个位置和它前面与后面取 LCP ,其中较大的长度设为 d ,表示从 sa[ i ] 位置开始的子串的右端点要在 sa[ i ]+d-1 位置之后才是只出现了一次的。

那么 sa[ i ] ~ sa[ i ]+d 位置的答案可以对 d+1 取 min ;至于 sa[ i ]+d+1 ~ n 位置,sa[ i ]可能成为它们答案的开头位置,所以可以维护每个位置备选答案串开头的最靠后位置(这样最靠近自己),让 sa[ i ]+d+1 ~ n 位置的这个值对 sa[ i ] 取 max 就行了。可以用线段树维护。

注意那个“备选答案串开头的最靠后位置”的初值不是0!不然第一个位置有可能和第0个位置组合了。应该是 -n 之类,才能排除影响。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls Ls[cr]
#define rs Rs[cr]
using namespace std;
const int N=5e5+5,M=N<<1;
int n,sa[N],rk[N],tp[N],tx[N],ht[N];
int tot,Ls[M],Rs[M],tg1[M],tg2[M];
int ans[N]; char s[N];
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
void Rsort(int n,int nm)
{
  for(int i=1;i<=nm;i++)tx[i]=0;
  for(int i=1;i<=n;i++)tx[rk[i]]++;
  for(int i=2;i<=nm;i++)tx[i]+=tx[i-1];
  for(int i=n;i;i--)sa[tx[rk[tp[i]]]--]=tp[i];
}
void get_sa(int n)
{
  int nm=150;
  for(int i=1;i<=n;i++)tp[i]=i,rk[i]=s[i];
  Rsort(n,nm);
  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[i]>k)tp[++tot]=sa[i]-k;
      Rsort(n,nm);memcpy(tp,rk,sizeof rk);
      nm=1;rk[sa[1]]=1;
      for(int i=2,u,v;i<=n;i++)
    {
      u=sa[i]+k;v=sa[i-1]+k;if(u>n)u=0;if(v>n)v=0;
      rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[u]==tp[v])?nm:++nm;
    }
      if(nm==n)break;
    }
}
void get_ht(int n)
{
  for(int i=1,k=0,j;i<=n;i++)
    {
      for(k?k--:0,j=sa[rk[i]-1];i+k<=n&&j+k<=n&&s[i+k]==s[j+k];k++);
      ht[rk[i]]=k;//rk[i]!!!
    }
}
void build(int l,int r,int cr)
{
  tg1[cr]=n+1; tg2[cr]=-n;////
  if(l==r)return; int mid=l+r>>1;
  ls=++tot; build(l,mid,ls);
  rs=++tot; build(mid+1,r,rs);
}
void mdfy(int l,int r,int cr,int L,int R,int k)
{
  if(l>=L&&r<=R){tg1[cr]=Mn(tg1[cr],k);return;}
  int mid=l+r>>1;
  if(L<=mid)mdfy(l,mid,ls,L,R,k);
  if(mid<R)mdfy(mid+1,r,rs,L,R,k);
}
void mdfyx(int l,int r,int cr,int L,int R,int k)
{
  if(l>=L&&r<=R){tg2[cr]=Mx(tg2[cr],k);return;}
  int mid=l+r>>1;
  if(L<=mid)mdfyx(l,mid,ls,L,R,k);
  if(mid<R)mdfyx(mid+1,r,rs,L,R,k);
}
void dfs(int l,int r,int cr,int lj1,int lj2)
{
  lj1=Mn(lj1,tg1[cr]); lj2=Mx(lj2,tg2[cr]);//before l==r
  if(l==r){ans[l]=Mn(lj1,l-lj2+1);return;}
  int mid=l+r>>1;
  dfs(l,mid,ls,lj1,lj2); dfs(mid+1,r,rs,lj1,lj2);
}
int main()
{
  scanf("%s",s+1);n=strlen(s+1);
  get_sa(n); get_ht(n);
  tot=1;build(1,n,1);
  for(int i=1,d;i<=n;i++)
    {
      d=sa[i]+Mx(ht[i],ht[i+1]); if(d>n)continue;
      mdfy(1,n,1,sa[i],d,d-sa[i]+1);
      if(d<n)mdfyx(1,n,1,d+1,n,sa[i]);
    }
  dfs(1,n,1,n+1,-n);//-n
  for(int i=1;i<=n;i++)printf("%d
",ans[i]);
  return 0;
}

 

以上是关于bzoj 2865 字符串识别——后缀数组的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1396识别子串&BZOJ2865字符串识别(后缀自动机)

BZOJ1396&2865 识别子串 后缀自动机 + 线段树

BZOJ 1396&&2865 识别子串[后缀自动机 线段树]

_bzoj1031 [JSOI2007]字符加密Cipher后缀数组

[BZOJ1031][JSOI2007]字符加密Cipher(后缀数组)

BZOJ4556字符串(后缀数组,主席树)