后缀数组:

一、模板:
①倍增nlogn:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=1000005;
int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
char str[maxn];
int N,M;
void Qsort(void)
{
    for(int i=0;i<=M;i++) tax[i]=0;
    for(int i=1;i<=N;i++) tax[rak[i]]++;
    for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
    for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}

bool cmp(int *f,int x,int y,int w)
{
    return f[x]==f[y]&&f[x+w]==f[y+w];
}


void suffixsort(void)
{
    M=128;
    for(int i=1;i<=N;i++) rak[i]=str[i],tp[i]=i;
    Qsort();

    for(int w=1,p=0;p<N;M=p,w<<=1)
    {
        p=0;
        for(int i=1;i<=w;i++) tp[++p]=N-w+i;
        for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;

        Qsort();
        swap(tp,rak);
        rak[sa[1]]=p=1;
        for(int i=2;i<=N;i++)
            rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
    }
}

void getheight(void)
{
    int k=0;
    for(int i=1;i<=N;i++)
    {
        if(k) k--;
        int j=sa[rak[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rak[i]]=k;
    }
}

int main(void)
{
    scanf("%s",str+1);
    N=strlen(str+1);
    suffixsort();
    getheight();
    for(int i=1;i<=N;i++)
    {
        if(i!=1) putchar(' ');
        printf("%d",sa[i]);
    }
    putchar('\n');
    return 0;
}

②DC3算法:n

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
using namespace std;
const int maxn=1000005;
int wa[maxn*3],wb[maxn*3],wv[maxn*3],wss[maxn*3];
int r[maxn*3],sa[maxn*3],height[maxn],rak[maxn];
char str[maxn*3];
int N,M;

int c0(int *r,int a,int b)
{
    return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
}

int c12(int k,int *r,int a,int b)
{
    if(k==2)
        return r[a]<r[b]||(r[a]==r[b]&&c12(1,r,a+1,b+1));
    else return r[a]<r[b]||(r[a]==r[b]&&wv[a+1]<wv[b+1]);
}

void Qsort(int *r,int *a,int *b,int n,int m)
{
    for(int i=0;i<n;i++) wv[i]=r[a[i]];
    for(int i=0;i<m;i++) wss[i]=0;
    for(int i=0;i<n;i++) wss[wv[i]]++;
    for(int i=1;i<m;i++) wss[i]+=wss[i-1];
    for(int i=n-1;i>=0;i--)
        b[--wss[wv[i]]]=a[i];
}

void dc3(int *r,int *sa,int n,int m)
{
    int i,j,*rn=r+n;
    int *san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
    r[n]=r[n+1]=0;
    for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
    Qsort(r+2,wa,wb,tbc,m);
    Qsort(r+1,wb,wa,tbc,m);
    Qsort(r,wa,wb,tbc,m);

    rn[F(wb[0])]=0,p=1;
    for(i=1;i<tbc;i++)
        rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;

    if(p<tbc) dc3(rn,san,tbc,p);
    else for(i=0;i<tbc;i++) san[rn[i]]=i;

    for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
    if(n%3==1) wb[ta++]=n-1;
    Qsort(r,wb,wa,ta,m);

    for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
    for(i=0,j=0,p=0;i<ta&&j<tbc;p++)
        sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];

    for(;i<ta;p++) sa[p]=wa[i++];
    for(;j<tbc;p++) sa[p]=wb[j++];
}

void da(int str[],int sa[],int rak[],int height[],int n,int m)
{
    for(int i=n;i<n*3;i++)
        str[i]=0;

    dc3(str,sa,n+1,m);

    int k=0;
    for(int i=0;i<=n;i++) rak[sa[i]]=i;

    for(int i=0;i<n;i++)
    {
        if(k) k--;
        int j=sa[rak[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rak[i]]=k;
    }
}


int main(void)
{
    scanf("%s",str);
    N=strlen(str);
    for(int i=0;i<N;i++)
        r[i]=str[i];
    M=128;
    da(r,sa,rak,height,N,M);
    for(int i=1;i<=N;i++)
    {
        if(i!=1) putchar(' ');
        printf("%d",sa[i]+1);
    }
    putchar('\n');
    return 0;
}

二、例题:
①本质不同的子串的个数:
每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相同的前缀的个数。
每个后缀suffix(sa(i))的贡献是len-sa(i)+1,但是有子串重复,重复的就是height(i)个与前面相同的前缀,那么一个后缀suffix(sa(i))的贡献就是len-sa(i)+1-height(i)。
也可以由公式求出所有子串,减去重复的子串sum(height(i))

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=100005;
int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
char str[maxn];
int N,M;
void Qsort(void)
{
    for(int i=0;i<=M;i++) tax[i]=0;
    for(int i=1;i<=N;i++) tax[rak[i]]++;
    for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
    for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}

bool cmp(int *f,int x,int y,int w)
{
    return f[x]==f[y]&&f[x+w]==f[y+w];
}


void suffixsort(void)
{
    M=128;
    for(int i=1;i<=N;i++) rak[i]=str[i],tp[i]=i;
    Qsort();

    for(int w=1,p=0;p<N;M=p,w<<=1)
    {
        p=0;
        for(int i=1;i<=w;i++) tp[++p]=N-w+i;
        for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;

        Qsort();
        swap(tp,rak);
        rak[sa[1]]=p=1;
        for(int i=2;i<=N;i++)
            rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
    }
}

void getheight(void)
{
    int k=0;
    for(int i=1;i<=N;i++)
    {
        if(k) k--;
        int j=sa[rak[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rak[i]]=k;
    }
}

int main(void)
{
    int pp;
    scanf("%d",&pp);
    scanf("%s",str+1);
    N=strlen(str+1);
    suffixsort();
    getheight();
    ll n=((ll)N*(N+1))/2;
    ll m=0;
    for(int i=1;i<=N;i++)
        m+=height[i];
    printf("%lld\n",n-m);
    return 0;
}

②两个字符串的最长公共子串:
将两个字符串中间加一个未出现的字符连接起来,然后做一次后缀数组,得到height数组,如果height数组所代表的两串分别在分界符的左右两侧,就更新最大值。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=300005;
int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
char str[maxn];
int N,M;
void Qsort(void)
{
    for(int i=0;i<=M;i++) tax[i]=0;
    for(int i=1;i<=N;i++) tax[rak[i]]++;
    for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
    for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}

bool cmp(int *f,int x,int y,int w)
{
    return f[x]==f[y]&&f[x+w]==f[y+w];
}


void suffixsort(void)
{
    M=128;
    for(int i=1;i<=N;i++) rak[i]=str[i],tp[i]=i;
    Qsort();

    for(int w=1,p=0;p<N;M=p,w<<=1)
    {
        p=0;
        for(int i=1;i<=w;i++) tp[++p]=N-w+i;
        for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;

        Qsort();
        swap(tp,rak);
        rak[sa[1]]=p=1;
        for(int i=2;i<=N;i++)
            rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
    }
}

void getheight(void)
{
    int k=0;
    for(int i=1;i<=N;i++)
    {
        if(k) k--;
        int j=sa[rak[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rak[i]]=k;
    }
}

int main(void)
{

    while(scanf("%s",str+1)!=EOF)
    {
        N=strlen(str+1);
        M=128;
        str[N+1]=1;

        int n=N;

        scanf("%s",str+N+2);
        N=strlen(str+1);

        suffixsort();
        getheight();

        int maxx=0;
        for(int i=1;i<=N;i++)
        {
            if(sa[i]>=n+2&&sa[i-1]<=n||sa[i]<=n&&sa[i-1]>=n+2)
                maxx=max(maxx,height[i]);
        }
        printf("%d\n",maxx);

    }
    return 0;
}

③不可重叠最长重复子串:Musical Theme POJ - 1743:二分

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=300005;
int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
int str[maxn];
int N,M;
void Qsort(void)
{
    for(int i=0;i<=M;i++) tax[i]=0;
    for(int i=1;i<=N;i++) tax[rak[i]]++;
    for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
    for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}

bool cmp(int *f,int x,int y,int w)
{
    return f[x]==f[y]&&f[x+w]==f[y+w];
}


void suffixsort(void)
{

    for(int i=1;i<=N;i++) rak[i]=str[i],tp[i]=i;
    Qsort();

    for(int w=1,p=0;p<N;M=p,w<<=1)
    {
        p=0;
        for(int i=1;i<=w;i++) tp[++p]=N-w+i;
        for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;

        Qsort();
        swap(tp,rak);
        rak[sa[1]]=p=1;
        for(int i=2;i<=N;i++)
            rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
    }
}

void getheight(void)
{
    int k=0;
    for(int i=1;i<=N;i++)
    {
        if(k) k--;
        int j=sa[rak[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rak[i]]=k;
    }
}

bool check(int mid)
{
    for(int i=2;i<=N;i++)
    {
        while(i<=N&&height[i]<mid) i++;

        if(i>N) break;

        int minn=N;
        int maxx=0;

        minn=min(minn,sa[i-1]);
        maxx=max(maxx,sa[i-1]);
        while(i<=N&&height[i]>=mid)
        {
            minn=min(minn,sa[i]);
            maxx=max(maxx,sa[i]);
            i++;
        }
        if(maxx-minn>=mid) return true;
    }
    return false;
}

int main(void)
{

    int n;
    while(scanf("%d",&n),n)
    {
        str[0]=0;
        for(int i=1;i<=n;i++)
            scanf("%d",&str[i]);

        for(int i=1;i<n;i++)
            str[i]=str[i+1]-str[i]+89;
        str[n]=0;
        N=--n;
        M=180;

        suffixsort();
        getheight();

        int l=0,r=N;
        int ans=0,mid;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(check(mid)) ans=mid,l=mid+1;
            else r=mid-1;
        }
        if(ans<4)
            printf("0\n");
        else printf("%d\n",ans+1);
    }
    return 0;
}

④出现k次的可重叠的最长子串长度:Milk Patterns POJ - 3261:二分

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=1000005;
int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
int str[maxn];
int N,M;
int n,k;
void Qsort(void)
{
    for(int i=0;i<=M;i++) tax[i]=0;
    for(int i=1;i<=N;i++) tax[rak[i]]++;
    for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
    for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}

bool cmp(int *f,int x,int y,int w)
{
    return f[x]==f[y]&&f[x+w]==f[y+w];
}


void suffixsort(void)
{

    for(int i=1;i<=N;i++) rak[i]=str[i],tp[i]=i;
    Qsort();

    for(int w=1,p=0;p<N;M=p,w<<=1)
    {
        p=0;
        for(int i=1;i<=w;i++) tp[++p]=N-w+i;
        for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;

        Qsort();
        swap(tp,rak);
        rak[sa[1]]=p=1;
        for(int i=2;i<=N;i++)
            rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
    }
}

void getheight(void)
{
    int k=0;
    for(int i=1;i<=N;i++)
    {
        if(k) k--;
        int j=sa[rak[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rak[i]]=k;
    }
}

bool check(int mid)
{
    for(int i=2;i<=N;i++)
    {
        int cnt=1;

        while(i<=N&&height[i]<mid)
            i++;
        if(i>N) break;

        while(i<=N&&height[i]>=mid)
        {
            cnt++;
            i++;
        }

        if(cnt>=k) return true;

    }
    return false;
}

int main(void)
{

    while(scanf("%d%d",&n,&k)!=EOF)
    {
        int maxx=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&str[i]);
            maxx=max(maxx,str[i]);
        }


        str[n+1]=-1;//防止求height数组越界
        N=n;
        M=maxx;

        suffixsort();
        getheight();

        int l=0,r=N;
        int ans=0,mid;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(check(mid)) l=mid+1,ans=mid;
            else r=mid-1;
        }

        printf("%d\n",ans);
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值