(标题是用来钓鱼的<( ̄3 ̄)> )
重新回顾了一下SA的板子,感觉理解得更深了。细节看码。
#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a, i##end = b; i <= i##end; ++i)
#define per(i, a, b) for (int i = a, i##end = b; i >= i##end; --i)
#define rep0(i, a) for (int i = 0, i##end = a; i < i##end; ++i)
#define per0(i, a) for (int i = a-1; ~i; --i)
#define chkmin(a, b) a = std::min(a, b)
#define chkmax(a, b) a = std::max(a, b)
typedef long long ll;
const int maxn = 1111111;
char s[maxn];
int sa[maxn];
void build_sa(char *s, int *sa) {
static int t[maxn << 1], t2[maxn << 1], c[maxn], *x, *y, n, m;
// t,t2是两个数组(要开两倍,否则下面比较会溢出),x和y将会指向它们;c是基数排序的桶;n表示长度,m表示字符集大小
x = t, y = t2, n = strlen(s), m = 'z'+1;
// 往下,x存第一关键字的值
rep0(i, m) c[i] = 0; // 清空桶
rep0(i, n) c[x[i] = s[i]]++; // 将字符串转化到x数组中(初始情况下第一关键字即为单个字符)并放入桶中
rep(i, 1, m-1) c[i] += c[i-1]; // 前缀累加可以方便查询排名
per0(i, n) sa[--c[x[i]]] = i; // 把后缀按照排名(此时长度为1)放入后缀数组中,注意要倒过来,保证串短的在前
for (int k = 1; k < n; k <<= 1) { // 倍增(长度为2k)
int p = 0; // 一变量多用。用来当y数组的指针;之后表示新的字符集大小
rep(i, n-k, n-1) y[p++] = i; // 第二关键字为Φ的后缀位置先排
rep0(i, n) if (sa[i] >= k) y[p++] = sa[i]-k;
// 用sa来排第二关键字(不存在第二关键字为<k的位置)
rep0(i, m) c[i] = 0; // 跟前面类似
rep0(i, n) c[x[y[i]]]++;
rep(i, 1, m-1) c[i] += c[i-1];
per0(i, n) sa[--c[x[y[i]]]] = y[i];
// 注意一定要倒过来取排名,保证第二关键字排序结果正序
std::swap(x, y); // 快速交换数组(指针)
p = 1, x[sa[0]] = 0; // 安排排名第一的权值
rep(i, 1, n-1)
x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k] ? p-1 : p++;
// 比对第一关键字和第二关键字
if ((m = p) == n) return; // 如果字符集=n,显然没有必要再排了
}
}
int main() {
scanf("%s", s);
build_sa(s, sa);
rep0(i, strlen(s)) printf("%d ", sa[i]+1);
return 0;
}