1. 程式人生 > >後綴數組(SA)

後綴數組(SA)

字符串 快速 c++程序 amp 思想 spa isdigit cor orz

學習了LRJ神犇的代碼。orz。

首先真心建議了解下基數排序!!且要有一定的c++程序經驗,否則程序很難看懂。

然後對著下面的程序調試(假裝你已經會了算法思想)

弄個一個禮拜一下午就能學會了。

該算法基於倍增,然後錯位比較,得到二元對並排序。

具體待更。

代碼如下:

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 #define re register
 6 #define rep(i, a, b) for (re int i = a; i <= b; ++i)
 7 #define
repd(i, a, b) for (re int i = a; i >= b; --i) 8 #define For(i, a, b, s) for (re int i = a; i <= b; s) 9 #define maxx(a, b) a = max(a, b) 10 #define minn(a, b) a = min(a, b) 11 #define LL long long 12 #define INF (1 << 30) 13 14 inline int read() { 15 int w = 0, f = 1; char c = getchar();
16 while (!isdigit(c)) f = c == - ? -1 : f, c = getchar(); 17 while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ 0), c = getchar(); 18 return w * f; 19 } 20 21 const int maxn = 1e6 + 5; 22 23 char s[maxn]; 24 int t[maxn], t2[maxn], sa[maxn], c[maxn], n; 25 26 void
build_sa(int m) { // m表示字符集大小 27 int *x = t, *y = t2; // 這樣寫是個技巧,可以快速交換數組(實際上交換了數組地址) 28 rep(i, 0, m-1) c[i] = 0; 29 rep(i, 0, n-1) c[x[i] = s[i]]++; 30 rep(i, 1, m-1) c[i] += c[i-1]; 31 repd(i, n-1, 0) sa[--c[x[i]]] = i; // 到這完成了初始字符串的基數排序 32 for (register int k = 1; k <= n; k <<= 1) { 33 int p = 0; 34 rep(i, n-k, n-1) y[p++] = i; 35 rep(i, 0, n-1) if (sa[i] >= k) y[p++] = sa[i]-k; //這兩句完成了第二關鍵字的排序,而第二關鍵字為x[i]+k。 36 rep(i, 0, m-1) c[i] = 0; 37 rep(i, 0, n-1) c[x[y[i]]]++; 38 rep(i, 1, m-1) c[i] += c[i-1]; 39 repd(i, n-1, 0) sa[--c[x[y[i]]]] = y[i]; // 再完成第一關鍵字的排序 40 swap(x, y); // 交換數組,原來的y數組(對應後面的x)沒有用了 41 p = 1; x[sa[0]] = 0; // 根據原來的x數組(對應為y數組)修改現在的x數組 42 rep(i, 1, n-1) 43 x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k] ? p-1 : p++; 44 if (p == n) return; // 如果所有數兩兩不同,到目前為止sa一定唯一不變了,退出 45 m = p; // 修改字符集大小 46 } 47 } 48 49 int main() { 50 scanf("%s", s); 51 n = strlen(s); 52 build_sa((int)z+1); 53 rep(i, 0, n-1) printf("%d ", sa[i]+1); 54 return 0; 55 }

後綴數組(SA)