P3809 【模板】后缀排序 (后缀数组sa[])

题意:

读入一个长度为 n的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置。位置编号为 1到 n。

数据范围:n<=1e6

解法:

题目要求的是排名第x的后缀在原串中的位置,
其实就是后缀数组中的sa[]数组,算出来直接输出就行了

code:

#include
using namespace std;
struct SA{
    static const int N=1e6+5;
    char s[N];
    int sa[N],rk[N],oldrk[N<<1],id[N],px[N],cnt[N];
    int n;//字符串长度
    int m=300;//字符集大小
    bool cmp(int x,int y,int w){
        return oldrk[x]==oldrk[y]&&oldrk[x+w]==oldrk[y+w];
    }
    void getSA(){
        int i,p,w;
        for(i=1;i<=n;i++)cnt[rk[i]=s[i]]++;
        for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
        for(i=n;i>=1;i--)sa[cnt[rk[i]]--]=i;
        //
        for(w=1;w<n;w<<=1,m=p){
            for(p=0,i=n;i>n-w;i--)id[++p]=i;
            for(i=1;i<=n;i++)if(sa[i]>w)id[++p]=sa[i]-w;
            memset(cnt,0,sizeof cnt);
            for(i=1;i<=n;i++)cnt[px[i]=rk[id[i]]]++;
            for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
            for(i=n;i>=1;i--)sa[cnt[px[i]]--]=id[i];
            memcpy(oldrk,rk,sizeof rk);
            for(p=0,i=1;i<=n;i++){
                rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
            }
        }
        //
        for(int i=1;i<=n;i++)printf("%d ",sa[i]);
        puts("");
    }
}sa;
signed main(){
    scanf("%s",sa.s+1);
    sa.n=strlen(sa.s+1);
    sa.getSA();
    return 0;
}

你可能感兴趣的:(P3809 【模板】后缀排序 (后缀数组sa[]))