简单介绍:

在字符串处理当中,后缀树和后缀数组都是非常有力的工具。其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现,能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多。可以说,在信息学竞赛中后缀数组比后缀树要更为实用。

作用:

后缀数组能快速比较两个后缀的字典序大小,它还能求多个字符串两两的LCP。

实现:

后缀数组一般是用倍增做的,总的时间复杂度是:,空间复杂度是:

排序是用基数排序来实现的。

代码:

#include
using namespace std;
const int N=1e6+35;
int n,p,c[N],a[N],sa[N],x[N],y[N];
//sa[i]表示排名为i的后缀的起点
//c[]用于基数排序
char s[N];
void SA(int n,int m)
{
    for(int i=1;i<=m;i++)c[i]=0;//清空鸡排数组
    for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
    for(int i=1;i<=m;i++)c[i]+=c[i-1];//前缀和
    for(int i=n;i>0;i--)sa[c[x[i]]--]=i;//第一轮的sa数组
    for(int i=1;i;i=i*2)//倍增
    {
        p=0;
        for(int j=n-i+1;j<=n;j++)y[++p]=j;//倍增个数不够,要补0
        for(int j=1;j<=n;j++)
            if(sa[j]>i)y[++p]=sa[j]-i;
        for(int j=1;j<=m;j++)c[j]=0;//清空鸡排数组
        for(int j=1;j<=n;j++)c[x[y[j]]]++;
        for(int j=1;j<=m;j++)c[j]+=c[j-1];
        for(int j=n;j>0;j--)sa[c[x[y[j]]]--]=y[j];
        swap(x,y);//注意
        x[sa[1]]=1;
        p=2;//注意
        for(int j=2;j<=n;j++)
        {
            if(y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i])x[sa[j]]=p-1;//相同
            else x[sa[j]]=p++;//不相同
        }
        if(p>n)return;//全部不相同
        m=p;//小优化
    }
}
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++)a[i]=s[i];
    SA(n,233);
    for(int i=1;i<=n;i++)printf("%d ",sa[i]);
    return 0;
}

题目:1031: [JSOI2007]字符加密Cipher

作用2:

后缀数组求多个字符串两两的LCP。

这是怎么做的呢?思想是把多个字符串拼成一个串,两个串之间用“#”隔开。

height数组定义:定义height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀。

对于j和k,不妨设rank[j]<rank[k],则有以下性质:

suffix(j)和suffix(k)的最长公共前缀为:

height[rank[j]+1], height[rank[j]+2], height[rank[j]+3], …, height[rank[k]]中的最小值。

所以这个可以用倍增即st表来完成。

题目:[P1117 [NOI2016]优秀的拆分

代码:

#include
using namespace std;
const int N=3e5+5;
char a[N];
int T,n,p,Log[N],f[N],g[N];
struct node{
    int sa[N],rk[N],height[N],c[N],x[N],y[N],st[N][25];
    void SA(int n,int m)
    {
        memset(sa,0,sizeof(sa));
        memset(rk,0,sizeof(rk));
        memset(x,0,sizeof(x));
        memset(y,0,sizeof(y));
        for(int i=1;i<=m;i++)c[i]=0;
        for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
        for(int i=1;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i;i--)sa[c[x[i]]--]=i;
        for(int i=1;i;i=i*2)
        {
            p=0;
            for(int j=n-i+1;j<=n;j++)y[++p]=j;
            for(int j=1;j<=n;j++)
                if(sa[j]>i)y[++p]=sa[j]-i;
            for(int j=1;j<=m;j++)c[j]=0;
            for(int j=1;j<=n;j++)c[x[y[j]]]++;
            for(int j=1;j<=m;j++)c[j]+=c[j-1];
            for(int j=n;j;j--)sa[c[x[y[j]]]--]=y[j];
            swap(x,y);
            x[sa[1]]=1;
            p=2;
            for(int j=2;j<=n;j++)
            {
                if(y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i])x[sa[j]]=p-1;
                else x[sa[j]]=p++;
            }
            if(p>n)return;
            m=p;
        }
    }
    void Height()//求出height数组,height[i]=lcp(sa[i-1],sa[i])
    {
        memset(height,0,sizeof(height));
        int k=0;
        for(int i=1;i<=n;i++)rk[sa[i]]=i;//rk[i]表示起点在i的后缀的排名
        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&&a[i+k]==a[j+k])k++;
            height[rk[i]]=k;
        }
    }
    void ST()//倍增求min{height[]}
    {
        memset(st,0x3f,sizeof(st));
        for(int i=1;i<=n;i++)st[i][0]=height[i];
        for(int i=1;i<=15;i++)
            for(int j=1;j<=n;j++)
                st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
    }
    int query(int l,int r)
    { 
        int L=l,R=r; 
        l=min(rk[L],rk[R])+1;
        r=max(rk[L],rk[R]);
        int t=Log[r-l+1]; 
        return min(st[l][t],st[r-(1<<t)+1][t]); 
    } 
}A,B;
void work()
{                    
    memset(f,0,sizeof(f));              
    memset(g,0,sizeof(g));
    scanf("%s",a+1);
    n=strlen(a+1);
    A.SA(n,122);
    A.Height();
    A.ST();
    reverse(&a[1],&a[n+1]);
    B.SA(n,122);
    B.Height();
    B.ST();
    for(int len=1;len<=n/2;len++)
    {
        for(int i=len,j=i+len;j<=n;i+=len,j+=len)
        {
            int lcp=min(A.query(i,j),len),lcs=min(B.query(n-i+2,n-j+2),len-1);
            int t=lcp+lcs-len+1;
            if(lcp+lcs>=len)
            {
                g[i-lcs]++;g[i-lcs+t]--;
                f[j+lcp-t]++;f[j+lcp]--;
            }
        }
    }
    for(int i=1;i<=n;i++)f[i]+=f[i-1],g[i]+=g[i-1]; 
    long long ans=0; 
    for(int i=1;i<n;i++)ans+=1ll*f[i]*g[i+1]; 
    printf("%lld\n",ans);     
}
int main()
{
    Log[0]=-1;
    for(int i=1;i>1]+1;
    scanf("%d",&T);
    while(T--)work();
    return 0;
}

题目:P4341 [BJWC2010]外星联络

代码:

#include
using namespace std;
const int N=1e6+5;
int n,a[N],x[N],y[N],c[N],sa[N],rk[N],height[N];
char s[N];
/*char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}*/
#define gc getchar
inline int read()
{
    int ret=0,f=0;char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){ret=ret*10+c-48;c=gc();}
    if(f)return -ret;return ret;
}
void SA(int n,int m)
{
    int p;
    for(int i=1;i<=m;i++)c[i]=0;
    for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
    for(int i=1;i<=m;i++)c[i]+=c[i-1];
    for(int i=n;i;i--)sa[c[x[i]]--]=i;
    for(int i=1;i;i=i*2)
    {
        p=0;
        for(int j=n-i+1;j<=n;j++)y[++p]=j;
        for(int j=1;j<=n;j++)
            if(sa[j]>i)y[++p]=sa[j]-i;
        for(int j=1;j<=m;j++)c[j]=0;
        for(int j=1;j<=n;j++)c[x[y[j]]]++;
        for(int j=1;j<=m;j++)c[j]+=c[j-1];
        for(int j=n;j;j--)sa[c[x[y[j]]]--]=y[j];
        swap(x,y);
        p=2;
        x[sa[1]]=1;
        for(int j=2;j<=n;j++)
            if(y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i])x[sa[j]]=p-1;
            else x[sa[j]]=p++;
        if(p>n)return;
        m=p;
    }
}
void Height()
{
    int k=0;
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    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&&a[i+k]==a[j+k])k++;
        height[rk[i]]=k;
    }
}
int main()
{
    n=read();
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)a[i]=s[i];
    SA(n,233);
    Height();
    for(int i=2;i<=n;i++)
    {
        for(int j=height[i-1]+1;j<=height[i];j++)
        {
            int k=i;
            while(height[k]>=j)k++;
            printf("%d\n",k-i+1);
        }
    }
    return 0;
}