后缀数组代码浅析

· · 算法·理论

本文只是对后缀数组代码的分析,并无对思路的讲解,建议没有基础的同学不要阅读本文。

变量定义:

综述

Part 1 求出初始的 sa_i

for(int i=1;i<=n;i++)cnt[rk[i]=s[i]]++;
for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=1;i<=n;i++)sa[cnt[rk[i]]--]=i;

Part 2 通过 rk_isa_i

int now=0;
for(int i=n-k+1;i<=n;i++)ls[++now]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)ls[++now]=sa[i]-k;
for(int i=1;i<=m;i++)cnt[i]=0;
for(int i=1;i<=n;i++)cnt[rk[i]]++;
for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[rk[ls[i]]]--]=ls[i];

Part 3 由 sa_i 求出 rk_i

swap(ls,rk);m=0;
for(int i=1;i<=n;i++)rk[sa[i]]=(ls[sa[i]]==ls[sa[i-1]]&&ls[sa[i]+k]==ls[sa[i-1]+k])?m:++m;
if(m==n) break;

完整代码如下。

#include<bits/stdc++.h>
using namespace std;
int sa[1000001],rk[1000001],ls[1000001],cnt[1000001];
int main(){
//  freopen("P3809_2.in","r",stdin);
    string s;cin>>s;int n=s.size();s=" "+s;int m=128;
    for(int i=1;i<=n;i++)cnt[rk[i]=s[i]]++;
    for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
    for(int i=1;i<=n;i++)sa[cnt[rk[i]]--]=i;
    for(int k=1;k<=n;k<<=1){
        int now=0;
        for(int i=n-k+1;i<=n;i++)ls[++now]=i;
        for(int i=1;i<=n;i++)if(sa[i]>k)ls[++now]=sa[i]-k;
        for(int i=1;i<=m;i++)cnt[i]=0;
        for(int i=1;i<=n;i++)cnt[rk[i]]++;
        for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
        for(int i=n;i>=1;i--)sa[cnt[rk[ls[i]]]--]=ls[i];
        swap(ls,rk);m=0;
        for(int i=1;i<=n;i++)rk[sa[i]]=(ls[sa[i]]==ls[sa[i-1]]&&ls[sa[i]+k]==ls[sa[i-1]+k])?m:++m;
        if(m==n) break;
    }
    for(int i=1;i<=n;i++)
        cout<<sa[i]<<" ";
    return 0;
}