【笔记】后缀数组
后缀数组 (SA)
0 约定
字符串下标从
字符串
「后缀
1 定义
这个字符串的所有非空后缀按字典序(用 ASCII 数值比较)从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置。
后缀数组,
排名数组,
显然,这两个数组满足
2 求解
2.1 思想
先谈更 Native 的倍增。她较线性的 DC3 优势在于实现容易,常数小,空间复杂度小,而时间复杂度也只多一个
字典序总是具有可以贪心的性质,从贪心入手,类比 ST 表,我们另记
然后,我们枚举
2.2 实现
2.2.1 易错
- 注意
的右端点是很可能超过 的,但是因为 后面都是\0
,比所有的小写字母都小,所以没有影响。如果多测就要注意了。
2.2.2 过程
- 首先对
2.3 Code
/*
* Time Spent:
* Solution:
Tag:
* Summary:
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
char s[N];
int n, sa[N], rk[N], w[N], sw[N], x[N], y[N];
int main () {
#ifdef LOCAL
freopen("_1.in", "r", stdin);
freopen("_1.out", "w", stdout);
#else
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
#endif
cin >> s+1, n = strlen(s+1);
int m = max(n, 1000);
for (int i = 1; i <= n; i++) w[s[i]]++;
for (int i = 1; i <= m; i++) sw[i] = sw[i-1] + w[i];
for (int i = 1; i <= n; i++) x[sw[s[i]]--] = i;
for (int i = 1; i <= n; i++)
rk[x[i]] = rk[x[i-1]] + !(s[x[i]] == s[x[i-1]]);
for (int j = 1; j <= n; j <<= 1) {
memset(w, 0, sizeof w);
for (int i = 1; i <= n; i++) w[rk[i+j]]++;
sw[0] = w[0];
for (int i = 1; i <= m; i++) sw[i] = sw[i-1] + w[i];
for (int i = 1; i <= n; i++) x[sw[rk[i+j]]--] = i;
memset(w, 0, sizeof w);
for (int i = 1; i <= n; i++) w[rk[i]]++;
for (int i = 1; i <= m; i++) sw[i] = sw[i-1] + w[i];
for (int i = n; i; i--)
y[sw[rk[x[i]]]--] = x[i];
static int r[N];
memset(r, 0, sizeof r);
for (int i = 1; i <= n; i++)
r[y[i]] = r[y[i-1]] + !(rk[y[i]] == rk[y[i-1]] && rk[y[i]+j] == rk[y[i-1]+j]);
memcpy(rk, r, sizeof r);
}
for (int i = 1; i <= n; i++) sa[rk[i]] = i;
for (int i = 1; i <= n; i++) cout << sa[i] << ' ';
return 0;
}
/*
g++ _1.cpp -o _1 -O2 -std=c++14 -DLOCAL; ./_1.exe
*/
对于一个子串,出现它的后缀的排名是一段连续的区间。可以二分左右边界求得。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】