# O(N) Suffix Array
**Người viết:**
> **Nguyễn Tường Duy - VNU University of Engineering and Technology**
## Bài toán
Cho xâu $S$ độ dài $n$ thỏa mãn $S[i] \leq n$ ($1 \leq i \leq n$), sắp xếp các hậu tố của $S$ theo thứ tự từ điển (hậu tố của $S$ được biểu diễn bằng vị trí $i$ tương ứng với $S[i, n]$), gọi mảng đã sắp xếp đó là mảng hậu tố.
## Skew Algorithm
Ý tưởng của thuật toán là:
**Bước 1.** Sắp xếp các hậu tố ở vị trí $i$ mod $3 \neq 0$.
**Bước 2.** Sắp xếp các hậu tố ở vị trí $i$ mod $3 = 0$ sử dụng kết quả của bước 1.
**Bước 3.** Gộp mảng hậu tố ở bước $1$ và $2$ bằng thuật toán merge sort.
### Bước 1
Đầu tiên ta sắp xếp các hậu tố ở vị trí $i$ mod $3 \neq 0$ theo giá trị của hậu tố $i$ là $S[i, i + 2]$. Ta sẽ dùng thuật toán counting sort để sắp xếp trong thời gian $O(n)$.
Nếu các giá trị của hậu tố phân biệt thì ta đã hoàn thành bước $1$. Nếu không thì ta sẽ sắp xếp các hậu tố này theo cách đệ quy.
Cụ thể hơn ta có mảng $S_{12} = [1, 4, 7, \dots, 2, 5, 8, \dots]$, ta sẽ sắp xếp $S_{12}$ theo các giá trị sau: $[S[1, 3], S[4, 6], S[7, 9], \dots, -\infty, S[2, 4], S[5, 7], S[8, 10], \dots]$ (thêm giá trị $-\infty$ vào giữa để ngăn cách hậu tố $i$ mod $3 = 1$ với $i$ mod $3 = 2$).
Sau khi sắp xếp xong ta sẽ có mảng $e$ là giá trị tương đương của các hậu tố ($e$ thỏa mãn hậu tố $i <$ hậu tố $j$ thì $e[i] < e[j]$, hậu tố $i =$ hậu tố $j$ thì $e[i] = e[j]$). Ta sẽ tính đệ quy mảng hậu tố của mảng $[e[S_{12}[1]], e[S_{12}[2]], \dots]$. Ta thấy hậu tố của mảng $e$ tương đương với các hậu tố trong mảng $S_{12}$.
(ví dụ hậu tố $1$ là $[S[1], S[2], S[3], S[4], S[5], S[6], \dots] = [e[S[1, 3]], e[S[4, 6]], \dots]$)
### Bước 2
Ta sẽ sắp xếp các hậu tố ở vị trí $i$ mod $3 = 0$ theo giá trị của hậu tố $i$ là $S[i]$ và thứ tự của hậu tố $i + 1$ trong mảng hậu tố ở bước $1$. Ta dùng thuật toán counting sort để sắp xếp trong thời gian $O(n)$.
### Bước 3
Để gộp hai mảng hậu tố bằng thuật toán merge sort thì ta cần so sánh hậu tố $i$ mod $3 = 0$ với hậu tố $j$ mod $3 \neq 0$. Ta sẽ so sánh hai hậu tố trong thời gian $O(1)$ như sau:
* So sánh hậu tố $i$ mod $3 = 0$ và $j$ mod $3 = 1$ bằng cách so sánh $S[i]$ và $S[j]$, vị trí của hậu tố $i + 1$ và hậu tố $j + 1$ trong mảng hậu tố ở bước $1$.
* So sánh hậu tố $i$ mod $3 = 0$ và $j$ mod $3 = 2$ bằng cách so sánh $S[i]$ và $S[j]$, so sánh $S[i + 1]$ và $S[j + 1]$, vị trí của hậu tố $i + 2$ và hậu tố $j + 2$ trong mảng hậu tố ở bước $1$.
## Tổng kết
Ta sẽ phân tích độ phức tạp thời gian ở từng bước:
* **Bước 1:** $O(n + \frac{2}{3} \cdot n + \frac{4}{9} \cdot n + \dots) = O(n)$
* **Bước 2:** $O(n)$
* **Bước 3:** $O(n)$
Như vậy ta được thuật toán có độ phức tạp thời gian $O(n)$.
## Cài đặt
Những lưu ý khi code:
* $n = 1$ thì mảng hậu tố là $[0]$.
* Ta sẽ thêm $2$ phần tử $0$ vào cuối xâu $S$ để code dễ hơn.
* Khi sắp xếp đệ quy mảng hậu tố ở bước $1$ thì ta sẽ thêm giá trị tương ứng với $-\infty$ giữa hậu tố $i$ mod $3 = 1$ với $i$ mod $3 = 2$.
Code:
``` cpp=
void counting_sort(vector<int> &arr, const vector<int>::iterator &val) {
int ma = 0;
for (int &x: arr) ma = max(ma, val[x]);
vector<int> cnt(ma + 1);
for (int &x: arr) ++cnt[val[x]];
for (int i = 0, sum = 0; i <= ma; ++i) {
int tmp = cnt[i];
cnt[i] = sum;
sum += tmp;
}
vector<int> new_arr(arr.size());
for (int &x: arr) new_arr[cnt[val[x]]++] = x;
arr = new_arr;
}
vector<int> suffix_array(vector<int> s) {
int n = s.size();
if (n == 1) return vector<int>(1);
s.resize(n + 2);
int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n12 = n1 + n2;
vector<int> sa12(n12);
for (int i = 0; i < n1; ++i) sa12[i] = i * 3 + 1;
for (int i = 0; i < n2; ++i) sa12[i + n1] = i * 3 + 2;
counting_sort(sa12, s.begin() + 2);
counting_sort(sa12, s.begin() + 1);
counting_sort(sa12, s.begin());
vector<int> equiv(n + 1);
equiv[sa12[0]] = 2;
for (int i = 1; i < n12; ++i) equiv[sa12[i]] = equiv[sa12[i - 1]] + (s[sa12[i]] != s[sa12[i - 1]] ||
s[sa12[i] + 1] != s[sa12[i - 1] + 1] ||
s[sa12[i] + 2] != s[sa12[i - 1] + 2]);
if (equiv[sa12[n12 - 1]] <= n12) {
vector<int> s12(n12 + 1);
for (int i = 0; i < n1; ++i) s12[i] = equiv[i * 3 + 1];
s12[n1] = 1;
for (int i = 0; i < n2; ++i) s12[i + n1 + 1] = equiv[i * 3 + 2];
sa12 = suffix_array(s12);
sa12.erase(sa12.begin());
for (int &x: sa12) x = x < n1 ? x * 3 + 1 : (x - n1) * 3 - 1;
equiv[sa12[0]] = 2;
for (int i = 1; i < n12; ++i) equiv[sa12[i]] = equiv[sa12[i - 1]] + 1;
}
vector<int> sa0(n0);
for (int i = 0; i < n0; ++i) sa0[i] = i * 3;
counting_sort(sa0, equiv.begin() + 1);
counting_sort(sa0, s.begin());
vector<int> sa(n);
int i = 0, j = 0, k = 0;
while (i < n12 && j < n0) {
int suff12 = sa12[i], suff0 = sa0[j];
while (true) {
if (s[suff12] != s[suff0]) {
sa[k++] = s[suff12] < s[suff0] ? sa12[i++] : sa0[j++];
break;
}
++suff12, ++suff0;
if (equiv[suff12] && equiv[suff0]) {
sa[k++] = equiv[suff12] < equiv[suff0] ? sa12[i++] : sa0[j++];
break;
}
}
}
while (i < n12) sa[k++] = sa12[i++];
while (j < n0) sa[k++] = sa0[j++];
return sa;
}
```
## Tham khảo
https://www.cs.helsinki.fi/u/tpkarkka/publications/icalp03.pdf