后缀数组SA

算法问题描述

给定一个字符串s,按字典序排序s的所有子串

算法思想

鬼知道什么思想,好像没有什么思想。哦,想起来了,是倍增。
考虑最简单的后缀间 O(n) O ( n ) 比较和快排 O(nlogn) O ( n l o g n ) ,总复杂度 O(n2logn) O ( n 2 l o g n ) 。考虑优化字符串间的比较,用倍增的思想,假设 k/2 k / 2 长度的已经比完了,比较长度为 k k 的字符串(长度不足补0)只要比前 k/2 k / 2 段再比较后 k/2 k / 2 段,也就是把字符串当做一个二元组做双关键字排序,其中 k=1 k ≪= 1 。这样复杂度是 O(nlog2n) O ( n l o g 2 n ) 的。考虑到我们是对字符串排序,字符串长度为 n n ,则每个值的种类最多n种,所以可以用基数排序。于是复杂度来到 O(nlogn) O ( n l o g n )

细节

详见代码,细节太多

代码

void Qsort(){//对rak基数排序,结果记在sa里
    for(int i=0;i<=m;i++) tax[i]=0; //清零
    for(int i=1;i<=n;i++) tax[rak[tp[i]]]++; //第一关键字入桶
    for(int i=1;i<=m;i++) tax[i]+=tax[i-1]; //记名次
    for(int i=n;i;i--) sa[tax[rak[tp[i]]]--]=tp[i];//倒着取
    //使用tp[i]的原因是我们进行的是双关键字排序,用基数排序的性质来理解就是我们初始数组的下标是tp[i],值是rak[tp[i]],基数排序具有稳定性,可以保证值相同时按下标排
    //或者解释一下就是倒着取tp[i]可以使得第一关键字相同时第二关键字排后的先把桶里面大的名次取了用
    //x=rak[tp[i]]:第二关键字排第i位的后缀在第一关键字中的值
    //y=tax[x]--:取出来第一关键字的排名,下一个取的排名--
    //sa[y]=tp[i]:第一关键字排名y的后缀是第二关键字排i的后缀
void Ssort(){//后缀排序
    m=147;
    Qsort();
    for(int k=1,p=0;p<n&&k<=n;m=p,p=0,k<<=1){
    //p是排名计数器(排名可能一样,当排名出现n种的时候就可以退出了)
        for(int i=n-k+1;i<=n;i++) tp[++p]=i;
        //后k个后缀不存在第二关键字,排最前
        for(int i=1;i<=n;i++) if(sa[i]>k) tp[++p]=sa[i]-k;
        //sa是k=k/2时的sa,相当于第二关键字,如果有第二关键字,这个第二关键字的值就是现在sa[i]-k的
        Qsort();
        swap(rak,tp);//rak要重置,tp没用了,干脆用tp
        rak[sa[1]]=p=1;//重置排名
        for(int i=2;i<=n;i++){
            rak[sa[i]]=(tp[sa[i-1]]==tp[sa[i]] && tp[sa[i-1]+k]==tp[sa[i]+k])?p:++p;
            //基数排序不能重复,for比较相邻两个后缀是不是两个关键字都一样的
        }
    }
}

清爽版

void Qsort(){
    for(int i=0;i<=m;i++) tax[i]=0;
    for(int i=1;i<=n;i++) tax[rak[tp[i]]]++;
    for(int i=1;i<=m;i++) tax[i]+=tax[i-1];
    for(int i=n;i;i--) sa[tax[rak[tp[i]]]--]=tp[i];
void Ssort(){
    m=147;
    Qsort();
    for(int k=1,p=0;p<n&&k<=n;m=p,p=0,k<<=1){
        for(int i=n-k+1;i<=n;i++) tp[++p]=i;
        for(int i=1;i<=n;i++) if(sa[i]>k) tp[++p]=sa[i]-k;
        Qsort();
        swap(rak,tp);
        rak[sa[1]]=p=1;
        for(int i=2;i<=n;i++){
            rak[sa[i]]=(tp[sa[i-1]]==tp[sa[i]] && tp[sa[i-1]+k]==tp[sa[i]+k])?p:++p;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarden_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值