後綴數組SA超詳細講解

(標題是用來釣魚的<( ̄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], t2[maxn], 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]; // 前綴累加可以方便查詢排名
	rep0(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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章