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

Posted zinn

tags:

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

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

唯一出现的子串就是每个后缀除去和别的后缀最长的 LCP 之外的前缀;

所以用这个更新一段区间的答案,可以用线段树维护;

在 sa[i] ~ sa[i]+LCP+1 位置的答案由 LCP+1 更新,sa[i]+LCP+1 之后的位置就更新一个位置 ps 表示从 ps 到本位置的子串也可以;

最后两个取min即可;

注意如果整个后缀就是 LCP,那么就不能更新了;

线段树中 ps 的初值是0,取答案时注意判掉0。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid ((l+r)>>1)
using namespace std;
int const xn=5e5+5,xm=(xn<<1);
int n,m,rk[xn],sa[xn],tax[xn],tp[xn],ht[xn];
int cnt=1,ls[xm],rs[xm],len[xm],ps[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 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(); memcpy(tp,rk,sizeof rk);
      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]]=k;
    }
}
void build(int x,int l,int r)
{
  len[x]=n+1;
  if(l==r)return;
  ls[x]=++cnt; build(ls[x],l,mid);
  rs[x]=++cnt; build(rs[x],mid+1,r);
}
void update(int x,int l,int r,int L,int R,int v)
{
  if(l>=L&&r<=R){len[x]=min(len[x],v); return;}
  if(mid>=L)update(ls[x],l,mid,L,R,v);
  if(mid<R)update(rs[x],mid+1,r,L,R,v);
}
void chg(int x,int l,int r,int L,int R,int v)
{
  if(l>=L&&r<=R){ps[x]=max(ps[x],v); return;}
  if(mid>=L)chg(ls[x],l,mid,L,R,v);
  if(mid<R)chg(rs[x],mid+1,r,L,R,v);
}
int query(int x,int l,int r,int pos,int v)
{
  v=min(v,len[x]);
  if(l==r)return v;
  if(pos<=mid)return query(ls[x],l,mid,pos,v);
  else return query(rs[x],mid+1,r,pos,v);
}
int ask(int x,int l,int r,int pos,int v)
{
  v=max(v,ps[x]);
  if(l==r)return v;
  if(pos<=mid)return ask(ls[x],l,mid,pos,v);
  else return ask(rs[x],mid+1,r,pos,v);
}
int main()
{
  scanf("%s",s+1); n=strlen(s+1); m=125;
  work(); get(); build(1,1,n);
  for(int i=1;i<=n;i++)
    {
      int lcp=max(ht[i],ht[i+1]);
      if(sa[i]+lcp<=n)update(1,1,n,sa[i],sa[i]+lcp,lcp+1);//
      if(sa[i]+lcp+1<=n)chg(1,1,n,sa[i]+lcp+1,n,sa[i]);
    }
  for(int i=1;i<=n;i++)
    {
      int t=query(1,1,n,i,n+1),p=ask(1,1,n,i,0);
      if(p==0)printf("%d
",t);//!
      else printf("%d
",min(t,i-p+1));
    }
  return 0;
}

 

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

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

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

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

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

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

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