后缀数组学习笔记

前置

纯属博主虎的的
罗穗骞2009NOI集训队论文
还是原版的最明白啊
先了解基数排序和倍增求sa思想
并且有一定的看别人博客的基础(对,没错,就是这么不要脸)
基数排序=>博客
这里主要说一下代码的理解及重要点

重点及其目标

根据后缀子串,他们一定是两两不同的
rk和sa也是差不多反函数关系,那rk也是不重复的
所以我们最终的目标是求出rk中无重复时的sa数组,根据定义-最差倍增到n
总的来说,求sa的过程就是不断地用基数排序倍增直到rk无重复
而基数排序从代码中很容易看出
就是是用rk[]和x[]求出sa[]的过程(封装)
那如何用rk[]和x[]求出sa[]就成了重中之重
如果明白了这个就基本理解倍增求sa的过程了

分析目标&&正题

ps:这里基数排序按照从低到高排序
我们一共有俩关键字

FOR(i,n-k+1,n) x[++num]=i;
FOR(i,1,n) if (sa[i]>k) x[++num]=sa[i]-k;

这是弄出第二关键字排序的x[]的过程
利用了上次和这次之间的交错关系
然后有的地方第二关键字没有,也就是-INF,要放在前面,还是老话,画图去
求出第二关键字排完序的x[]
这就说明我们已经完成了基数排序的一半任务了
剩下的就是利用第一关键字来排序了

FOR(i,1,n) ++c[rk[i]];
FOR(i,2,m) c[i]+=c[i-1]; 
ROF(i,n,1) sa[c[rk[i]]--]=i; 

c[rk[x[i]]]-- 下标
x[i] 权值
这是根据for循环来臆测的
x[i]这是按照第二关键字排序的顺序来进行的,我们可以把x[i]看做j
也就是
c[rk[j]]-- 下标
j 权值
注意到,上下两层的第一关键字是不变的,那第一关键字就好弄了
我们用上一次的sa捣鼓出来,存在rank里面
所以就是sa[rk[j]]=j喽,但是,rk是会有重复的呀
而且我们还不知道c[]的作用呢
其实c[]就是统计个数来确定的sa的次序的
这样倍增范围是不断增大,那么重复数也会慢慢变少,直到无重复(定义)

然后后面就是一堆处理来保证下一次操作的正常运行以及判断结束
还算好理解,嗯

代码

#include <bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;++i)
#define ROF(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const int maxn=1000050;
char s[maxn];
int n,m,x[maxn],rk[maxn],c[maxn],sa[maxn];
void get_SA()
{
    FOR(i,1,n) ++c[rk[i]=s[i]];
    //rk[i]是第i个元素的第一关键字 
    FOR(i,2,m) c[i]+=c[i-1]; 
    //做c的前缀和,我们就可以得出每个关键字最多是在第几名 
    ROF(i,n,1) sa[c[rk[i]]--]=i; 
    for(int k=1;k<=n;k<<=1) {
        int num=0;
        //@按照第二关键字进行排序
        FOR(i,n-k+1,n) x[++num]=i;
        FOR(i,1,n) if (sa[i]>k) x[++num]=sa[i]-k;
        FOR(i,1,m) c[i]=0;
        FOR(i,1,n) ++c[rk[i]];
        FOR(i,2,m) c[i]+=c[i-1];
        //@依次取出x[i],按照第一关键字排序
        ROF(i,n,1) sa[c[rk[x[i]]]--]=x[i],x[i]=0;
        //@这里不用想太多,因为要生成新的rt时要用到旧的,就把旧的复制下来,没别的意思 
        swap(x,rk);
        //@制造新的rk
        rk[sa[1]]=1;num=1;
        FOR(i,2,n)
            rk[sa[i]]=(x[sa[i]]==x[sa[i-1]] && x[sa[i]+k]==x[sa[i-1]+k]) ? num : ++num;
        //判断
        if (num==n) break;
        m=num;
    }
    FOR(i,1,n) cout<<sa[i]<<" ";
}
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1),m=122;
    get_SA();
    return 0;
}
全部评论

相关推荐

一个菜鸡罢了:哥们,感觉你的简历还是有点问题的,我提几点建议,看看能不能提供一点帮助 1. ”新余学院“别加粗,课程不清楚是否有必要写,感觉版面不如拿来写一下做过的事情,教育经历是你的弱势就尽量少写 2. “干部及社团经历”和“自我评价”删掉 3. 论文后面的“录用”和“小修”啥的都删掉,默认全录用,问了再说,反正小修毕业前肯定能发出来 4. 工作经验和研究成果没有体现你的个人贡献,着重包装一下个人贡献
点赞 评论 收藏
分享
头像
11-21 11:39
四川大学 Java
是红鸢啊:忘了还没结束,还有字节的5k 违约金
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务