[补档计划] 后缀数组
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[补档计划] 后缀数组相关的知识,希望对你有一定的参考价值。
后缀数组的实现
对于一个字符串 S , 有后缀数组 sa[1..n] , 排名数组 rk[1..n], 和辅助数组 height[1..n].
sa[i]: 在 S 的后缀中, 排名第 i 的后缀为 S[sa[i]: n] .
rk[i]: 在 S 的后缀中, S[i:n] 的排名.
height[i]: S[sa[i-1]:n] 与 S[sa[i]:n] 的 LCP.
显然有 rk[sa[i]] = i, sa[rk[i]] = i.
使用倍增的方法快速求 sa[1..n] 和 rk[1..n] .
求 ht[1..n] 的时候, 我们依次处理 S[1:n], S[2:n], ..., S[n:n], 处理 S[i:n] 的时候求 ht[rk[i]] . Brute Force 的复杂度为 $O(n^2)$ , 但是我们可以利用一个性质: $ht[rk[i]] \\ge ht[rk[i-1]]-1$ , 附一个无字证明:
#include <cstdio> #include <cstring> #include <cstdlib> #define F(i, a, b) for (register int i = (a); i <= (b); i++) #define D(i, a, b) for (register int i = (a); i >= (b); i--) const int N = 50005; char s[N]; int n; int rk[N], sa[N], ht[N]; namespace Output { const int S = 1000000; char s[S]; char *t = s; inline void Print(int x) { if (x == 0) *t++ = ‘0‘; else { static int a[65]; int n = 0; for (; x > 0; x /= 10) a[++n] = x%10; while (n > 0) *t++ = ‘0‘+a[n--]; } *t++ = ‘ ‘; } inline void Flush(void) { fwrite(s, 1, t-s, stdout); } } using Output::Print; void Prework(void) { static int sum[N], trk[N], tsa[N]; int m = 500; F(i, 1, n) sum[rk[i] = s[i]]++; F(i, 1, m) sum[i] += sum[i-1]; D(i, n, 1) sa[sum[rk[i]]--] = i; rk[sa[1]] = m = 1; F(i, 2, n) rk[sa[i]] = (s[sa[i]] != s[sa[i-1]] ? ++m : m); for (int j = 1; m != n; j <<= 1) { int p = 0; F(i, n-j+1, n) tsa[++p] = i; F(i, 1, n) if (sa[i] > j) tsa[++p] = sa[i]-j; F(i, 1, n) sum[i] = 0, trk[i] = rk[i]; F(i, 1, n) sum[rk[i]]++; F(i, 1, m) sum[i] += sum[i-1]; D(i, n, 1) sa[sum[trk[tsa[i]]]--] = tsa[i]; rk[sa[1]] = m = 1; F(i, 2, n) { if (trk[sa[i]] != trk[sa[i-1]] || trk[sa[i]+j] != trk[sa[i-1]+j]) m++; rk[sa[i]] = m; } } m = 0; F(i, 1, n) { if (m > 0) m--; while (s[i+m] == s[sa[rk[i]-1]+m]) m++; ht[rk[i]] = m; } } int main(void) { #ifndef ONLINE_JUDGE freopen("xsy1621.in", "r", stdin); freopen("xsy1621.out", "w", stdout); #endif scanf("%s", s+1); n = strlen(s+1); Prework(); F(i, 1, n) Print(rk[i]); *(Output::t++) = ‘\\n‘; F(i, 1, n) Print(ht[i]); *(Output::t++) = ‘\\n‘; Output::Flush(); return 0; }
以上是关于[补档计划] 后缀数组的主要内容,如果未能解决你的问题,请参考以下文章