后缀数组(SA)
在看完洛谷大佬的
最详细讲解
以后,我还是不能说没有完全不懂,所以干脆自己写一篇后缀数组详解,造福后人(QAQ)
本篇讲解引用例子和图片来自某不知名视频资源的大佬,如有知道大佬姓名,会立刻回来标注的。
开始之前,先要了解这些数组是干嘛的,一定要记好。
一下以长度为8的字符串aabaaaab为例子,进行讲解
(好的,直接面向代码开始讲解)
一下以长度为8的字符串aabaaaab为例子,进行讲解
(好的,直接面向代码开始讲解)
这里是定义
const int N=2000010;
char s[N];
int n,m;//n为后缀个数, m为桶的个数
int x[N],y[N],c[N],sa[N],rk[N],height[N];
//桶数组x[i],辅助数组y[i],计数数组c[i]
这是按第一关键字(首字母)排序,对理解后面的代码很重要
//按第一个字母排序
for(i=1;i<=n;i++)c[x[i]=s[i]]++;
for(i=1;i<=m;i++)c[i]+=c[i-1];
for(i=n;i;i--)sa[c[x[i]]--]=i;
第一排代码,先执行n次复制\(x[i]\)和\(c[i]\),\(x[i]\)存放第i个后缀的第k为的字典序
\(c[i]\)表示存放字典序为i的后缀个数
\(y[i]\)备份上一次的\(sa[i]\)
其中核心语句
sa[c[x[i]]--]=i;
最为关键,一个for按后缀编号为序把每一个后缀的排序号给计算出来了。
淦,刚刚看到洛谷的题解提交通道以及关闭了,那我码这么多字干嘛!!!
直接上图,图中样例里有些行列没有标明是数组编号,还是数组存值,需要自己区分清楚,好好根据例子理解一下代码过程
还要去提升人身阅历了,拜拜~(QAQ)
点击查看代码
// Luogu P3809 【模板】后缀排序
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=2000010;
char s[N];
int n,m;//n为后缀个数, m为桶的个数
int x[N],y[N],c[N],sa[N],rk[N],height[N];
//桶数组x[i],辅助数组y[i],计数数组c[i]
void get_sa(){
int i,j,k;
//按第一个字母排序
for(i=1;i<=n;i++)c[x[i]=s[i]]++;
for(i=1;i<=m;i++)c[i]+=c[i-1];
for(i=n;i;i--)sa[c[x[i]]--]=i;
for(k=1;k<=n;k<<=1){ //logn轮
//按第二关键字排序
memset(c,0,sizeof(c));
for(i=1;i<=n;i++)y[i]=sa[i];
for(i=1;i<=n;i++)c[x[y[i]+k]]++;
for(i=1;i<=m;i++)c[i]+=c[i-1];
for(i=n;i;i--)sa[c[x[y[i]+k]]--]=y[i];
//按第一关键字排序
memset(c,0,sizeof(c));
for(i=1;i<=n;i++)y[i]=sa[i];
for(i=1;i<=n;i++)c[x[y[i]]]++;
for(i=1;i<=m;i++)c[i]+=c[i-1];
for(i=n;i;i--)sa[c[x[y[i]]]--]=y[i];
//把后缀放入桶数组
for(i=1;i<=n;i++)y[i]=x[i];
for(m=0,i=1;i<=n;i++)
if(y[sa[i]]==y[sa[i-1]]&&
y[sa[i]+k]==y[sa[i-1]+k])x[sa[i]]=m;
else x[sa[i]]=++m;
if(m==n)break;//已排好
}
}
void get_height(){
int i,j,k;
for(i=1;i<=n;i++)rk[sa[i]]=i;
for(i=1,k=0;i<=n;i++){ //枚举后缀i
if(rk[i]==1)continue;//第一名height为0
if(k)k--;//上一个后缀的height值减1
int j=sa[rk[i]-1];//找出后缀i的前邻后缀j
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
height[rk[i]]=k;
}
}
int main(){
scanf("%s",s+1);
n=strlen(s+1); m=122;
get_sa();
get_height();
for(int i=1;i<=n;i++)printf("%d ",sa[i]);
// puts("");
// for(int i=1;i<=n;i++)printf("%d ",height[i]);
return 0;
}
溜了,溜了