后缀数组(SA)
后缀数组
· 前言
· 定义
后缀数组分为两个数组:
易知:
· 算法流程
做法
我们考虑倍增,已知长度为
- 以前
个字符为第一关键字。
后 个字符为第二关键字。
排序的结果。
理解即可
rpt(i,1,n)sa[i]=i;
//此时 sa 的值不是正确的意义但仍可以保证正确
rpt(i,1,n)rk[i]=s[i];
//因为 rk 的值是正确的
for(int w=1;w<n;w<<=1){
//w 为上一组的字符串长度
sort(sa+1,sa+1+n,[&](int x,int y){
return rk[x]==rk[y]?rk[x+w]<rk[y+w]:rk[x]<rk[y];
});
//排名离散化
int now=0;
rpt(i,1,n){
if(rk[sa[i]]!=rk[sa[i-1]]||rk[sa[i]+w]!=rk[sa[i-1]+w])now++;
tmprk[sa[i]]=now;
//不能直接修改 rk
}
rpt(i,1,n)rk[i]=tmprk[i];
}
做法
我们考虑到
所以我们把排序换成基数排序即可。
优化前
int v=0;
rpt(i,1,n)v=max(v,(int)s[i]);
// v 表示基数排序值域
rpt(i,1,n)rk[i]=s[i];
rpt(i,0,v)t[i]=0;
rpt(i,1,n)t[rk[i]]++;
rpt(i,1,v)t[i]+=t[i-1];
rpt(i,1,n)sa[t[rk[i]]--]=i;
//此时 sa 的值需要表示正确的意义
for(int w=1;w<n;w<<=1){
//w 为上一组的字符串长度
rpt(i,0,v)t[i]=0;
rpt(i,1,n)t[rk[i+w]]++;
//以 rk[i+w] 为关键字
rpt(i,1,v)t[i]+=t[i-1];
rpt(i,1,n)sa[t[rk[i+w]]--]=i;
//第二关键字排序
rpt(i,0,v)t[i]=0;
rpt(i,1,n)id[i]=sa[i];
rpt(i,1,n)t[rk[id[i]]]++;
//以 rk[i] 为关键字
rpt(i,1,v)t[i]+=t[i-1];
tpr(i,n,1)sa[t[rk[id[i]]]--]=id[i];
//倒序枚举排名以保证相同大小按原顺序排序
//sa 会被改变所以用 id 存一下
//第一关键字排序
//排名离散化
int now=0;
rpt(i,1,n){
if(rk[sa[i]]!=rk[sa[i-1]]||rk[sa[i]+w]!=rk[sa[i-1]+w])now++;
tmprk[sa[i]]=now;
//不能直接修改 rk
}
v=now;
rpt(i,1,n)rk[i]=tmprk[i];
}
其实我们会发现上面代码有很多优化空间。
在上一层时 已经有序,所以不需要按第二关键字排序,只需考虑第二关键字为空的情况。
当 已经互不相同时,说明已经比出大小,直接结束即可。
优化后
for(int w=1;w<n;w<<=1){
//w 为上一组的字符串长度
int top=0;
rpt(i,n-w+1,n)id[++top]=i;
rpt(i,1,n)if(sa[i]-w>0)id[++top]=sa[i]-w;
rpt(i,1,n)sa[i]=id[i];
//第二关键字其实只需要考虑空串
//其他非空集已经有序
rpt(i,0,v)t[i]=0;
rpt(i,1,n)id[i]=sa[i];
rpt(i,1,n)t[rk[id[i]]]++;
rpt(i,1,v)t[i]+=t[i-1];
tpr(i,n,1)sa[t[rk[id[i]]]--]=id[i];
//第一关键字排序
//排名离散化
int now=0;
rpt(i,1,n){
if(rk[sa[i]]!=rk[sa[i-1]]||rk[sa[i]+w]!=rk[sa[i-1]+w])now++;
tmprk[sa[i]]=now;
//不能直接修改 rk
}
v=now;
rpt(i,1,n)rk[i]=tmprk[i];
if(v==n)break;
}
数组
定义
的求法
重要性质:
证明:
。
。
在排好序的后缀中:
设, 。
则有。
因为后缀必然存在。
所以且满足 。
即至多会减少一个字符。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律