芝士:后缀数组(SA)

例子

传送门

基数排序

它的工作原理是将待排序的元素拆分为k个关键字(比较两个元素时,先比较第一关键字,如果相同再比较第二关键字……),然后先对第k关键字进行稳定排序,再对第k-1关键字进行稳定排序,再对第k-2关键字进行稳定排序……最后对第一关键字进行稳定排序,这样就完成了对整个待排序序列的稳定排序。

​ --OI WIKI

过程

pace 1

考虑对两个字符串进行比较

定义其为\(s1,s2\)

如果\([0,i]\)已经能比较出大小,那么就不需要考虑\([i+1,lens)\)的关系了

反之,如果\([0,i]\)不能比较出大小,那么就需要考虑\([i+1,lens)\)的关系

pace 2

定义位置\(i\)的字符串为\([i,lens)\)的字符

比如\(abaaba\)

\[0:abaaba\\1:baaba\\2:aaba\\3:aba\\4:ba\\5:a\\ \]

考虑对于所有位置\(i\)字符串按第一位进行排序

那么排完序就会有

\[abaaba\\aaba\\aba\\a\\baaba\\ba \]

现在单独考虑第二位的大小,考虑到后缀之间的联系,现在真的需要知道每一个后缀具体是什么样子么?

位置\(0\)的字符串的第二位不就是位置\(1\)的字符串的第一位?

那么对于每一个位置都可以找到一个第一位与之第二位相对应

那么我们就可以知道只考虑第二位的后缀之间的大小关系

然后我们知道第二位的大小关系,又知道第一位的大小关系、

那么运用基数排序的思想,就可以知道前两位,即\([0,1]\)的大小关系

pace 3

按照pace2的内容进行递推,

如果我们知道只考虑前k位的大小关系,即\([0,k-1]\)的大小关系

现在我们想求出只考虑\([0,2k-1]\)的大小关系

考虑对于位置\(i\)的字符串,其\([k,2k-1]\)的字符就是位置\(i+k\)的前\(k\)

故可以知道只考虑\([k,2k-1]\)的大小关系

那么按照基数排序,就可以有\([0,2k-1]\)的大小关系

每一次基数排序的时间复杂度为\(O(n)\)

一共要倍增\(log_n\)

所以总的时间复杂度为\(O(nlog_n)\)

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
char s[1000005];
int lens,sa[1000005],m;//排名为i的位置&关键字的种类
int rk[1000005],tp[1000005];//位置i的排名&第二关键字排名为i的位置
int t[1000005],temp[1000005];//桶&临时数组
void ssort()//基数排序
{
    for(int i=1;i<=m;i++)
        t[i]=0;
    for(int i=1;i<=lens;i++)
        t[rk[i]]++;
    for(int i=2;i<=m;i++)
        t[i]+=t[i-1];
    for(int i=lens;i>=1;i--)
        sa[t[rk[tp[i]]]--]=tp[i];
}
void getsa()
{   
    for(int i=1;i<=lens;i++)
    {
        rk[i]=s[i];
        tp[i]=i;
    }
    ssort();
    for(int k=1;k<=lens;k<<=1)
    {
        int cnt=0;
        for(int i=lens-k+1;i<=lens;i++)
            tp[++cnt]=i;
        for(int i=1;i<=lens;i++)
            if(sa[i]>k)
                tp[++cnt]=sa[i]-k;
        ssort();
        swap(rk,tp);
        rk[sa[1]]=1;
        cnt=1;
        for(int i=2;i<=lens;i++)
            if(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])
                rk[sa[i]]=cnt;
            else
                rk[sa[i]]=++cnt;
        m=cnt;
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>(s+1);
    lens=strlen(s+1);
    m='z';
    getsa();
    for(int i=1;i<=lens;i++)
        cout<<sa[i]<<' ';
    return 0;
}

后缀的LCT

咕咕咕

posted @ 2020-12-19 18:29  loney_s  阅读(119)  评论(0)    收藏  举报