后缀数组学习笔记

后缀数组是一个处理字符串的有力工具,基本用途有模式串匹配和后缀最长公共前缀;例题
【JSOI 2007】【BZOJ 1031】字符加密ciper
后缀数组的躶体,现将字符串复制成环,然后输出 sa[i]<len s[sa[i]1] 即可,code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[250001];
int sa[250001];
int x[250001],y[250001],c[250001];
int n,maxm=0;
inline bool cmp(int *y, int a, int b, int k) {
    int arank1 = y[a];
    int brank1 = y[b];
    int arank2 = a + k >= n ? -1 : y[a + k];
    int brank2 = b + k >= n ? -1 : y[b + k];
    return arank1 == brank1 && arank2 == brank2;
}
void build_sa()
{
    int i,k;
    for (i=0;i<maxm;++i) c[i]=0;
    for (i=0;i<n;++i) c[x[i]=s[i]]++;
    for (i=1;i<maxm;++i) c[i]+=c[i-1];
    for (i=n-1;i>=0;--i) sa[--c[x[i]]]=i;
    for (k=1;k<=n;k<<=1)
      {
        int p=0;
        for (i=n-k;i<n;++i) y[p++]=i;
        for (i=0;i<n;++i) if (sa[i]>=k) y[p++]=sa[i]-k;
        for (i=0;i<maxm;++i) c[i]=0;
        for (i=0;i<n;++i) c[x[y[i]]]++;
        for (i=1;i<maxm;++i) c[i]+=c[i-1];
        for (i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1; x[sa[0]]=0;
        for (i=1;i<n;++i)
          x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
        if (p>=n) break;
        maxm=p;
      }
}
int main()
{
    int i;
    scanf("%s",&s);
    n=strlen(s);
    for (i=0;i<n;++i)
      {
        maxm=max(maxm,(int)(s[i]));
        s[i+n]=s[i];
      }
    n*=2; maxm++;
    build_sa();
    for (i=0;i<n;++i)
      if (sa[i]<(n/2))
      printf("%c",s[(sa[i]+(n/2)-1)%(n/2)]);
    printf("\n");
}

【AHOI 2013】【BZOJ 3238】差异
题目要求求出:

1i<jnlen(suffixi)+len(suffixj)2lcp(suffixi,suufixj)

我们在求出height数组之后,显然可以求出每个以 heighti 为lcp的左右边界 l,r ,然后我们对每个 i 算出它对答案的贡献,累加即可。
code:

#include<iostream> 
#include<cstdio> 
#include<cstring> 
#include<algorithm> 
#define inf 2100000000 
#define mid (l+r)/2 
#define lch i<<1,l,mid 
#define rch i<<1|1,mid+1,r 
using namespace std; 
char s[500001]; 
int n,ans,ansi,maxm; 
struct hp{ 
    int minn,mini; 
}seg[2000001]; 
struct he{ 
    int l,r; 
}range[500001]; 
int sa[500001],rank[500001],height[500001],x[500001],y[500001],c[500001]; 
long long pre[500001]; 
bool cmp(int *y,int i,int j,int k) 
{ 
    int a,b,c,d; 
    a=y[i]; b=y[j]; 
    c=i+k>=n?-1:y[i+k]; d=j+k>=n?-1:y[j+k]; 
    return a==b&&c==d; 
} 
void build_sa() 
{ 
    int i,j,k; 
    for (i=0;i<maxm;++i) c[i]=0;
    for (i=0;i<n;++i) c[x[i]=s[i]]++;
    for (i=1;i<maxm;++i) c[i]+=c[i-1];
    for (i=n-1;i>=0;--i) sa[--c[x[i]]]=i;
    for (k=1;k<=n;k<<=1)
      {
        int p=0;
        for (i=n-k;i<n;++i) y[p++]=i;
        for (i=0;i<n;++i) if (sa[i]>=k) y[p++]=sa[i]-k;
        for (i=0;i<maxm;++i) c[i]=0;
        for (i=0;i<n;++i) c[x[y[i]]]++;
        for (i=1;i<maxm;++i) c[i]+=c[i-1];
        for (i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1; x[sa[0]]=0;
        for (i=1;i<n;++i)
          x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
        if (p>=n) break;
        maxm=p;
      }
    for (i=0;i<n;++i) 
      rank[sa[i]]=i; 
    k=0; 
    for (i=0;i<n;++i) 
      { 
        if (!rank[i]) continue;
        if (k) k--; 
        j=sa[rank[i]-1];
        while (s[i+k]==s[j+k]) k++; 
        height[rank[i]]=k;   
      } 
} 
void updata(int i) 
{ 
    seg[i].minn=min(seg[i<<1].minn,seg[i<<1|1].minn); 
    if (seg[i<<1].minn<=seg[i<<1|1].minn) 
      seg[i].mini=seg[i<<1].mini; 
    else
      seg[i].mini=seg[i<<1|1].mini; 
} 
void build(int i,int l,int r) 
{ 
    if (l==r) 
      { 
        seg[i].minn=height[l]; 
        seg[i].mini=l; 
        return; 
      } 
    build(lch); build(rch); 
    updata(i); 
} 
void query(int i,int l,int r,int x,int y) 
{ 
    if (x<=l&&y>=r) 
      { 
        if (ans>seg[i].minn) 
          { 
            ans=seg[i].minn; 
            ansi=seg[i].mini; 
          } 
        return;   
      } 
    if (x<=mid) query(lch,x,y); 
    if (y>mid) query(rch,x,y); 
} 
void make_range(int l,int r) 
{ 
    int t; 
    ans=inf; 
    if (l>=r) return; 
    query(1,0,n-1,l+1,r); 
    t=ansi; 
    range[ansi].l=l; range[ansi].r=r; 
    make_range(l,t-1); 
    make_range(t,r); 
} 
int main() 
{ 
    int i; 
    long long ans=0; 
    scanf("%s",&s); 
    n=strlen(s); 
    for (i=0;i<n;++i)
      maxm=max(maxm,(int)(s[i]));
    maxm++;    
    build_sa(); 
    build(1,0,n-1); 
    make_range(0,n-1); 
    pre[0]=n-sa[0]; 
    for (i=1;i<n;++i) 
      pre[i]=(long long)(pre[i-1]+n-sa[i]); 
    for (i=1;i<n;++i) 
      ans+=(pre[i-1]-pre[range[i].l-1])*(long long)(range[i].r-i+1)+(pre[range[i].r]-pre[i-1])*(long long)(i-range[i].l)-2*(long long)(height[i]*(long long)((range[i].r-i+1)*(i-range[i].l)));   
    printf("%lld\n",ans); 
}

你可能感兴趣的:(后缀数组学习笔记)