for (int w = 1; w < n; w <<= 1) { // 现在得到 w * 2 时的 SA // (rk[x], rk[x + w]) for (int i = 0; i <= p + 1; ++i) cnt[i] = 0; for (int i = 1; i <= n; ++i) id[i] = sa[i], ++cnt[grk(i + w) + 1]; for (int i = 1; i <= p + 1; ++i) cnt[i] += cnt[i - 1]; for (int i = 1; i <= n; ++i) sa[++cnt[grk(id[i] + w)]] = id[i];
for (int i = 0; i <= p; ++i) cnt[i] = 0; for (int i = 1; i <= n; ++i) id[i] = sa[i], ++cnt[rk[i]]; for (int i = 1; i <= p; ++i) cnt[i] += cnt[i - 1]; for (int i = 1; i <= n; ++i) sa[++cnt[rk[id[i]] - 1]] = id[i]; swap(ork, rk); p = 0; for (int i = 1; i <= n; ++i) { if (i && ork[sa[i]] == ork[sa[i - 1]] && gork(sa[i] + w) == gork(sa[i - 1] + w)) rk[sa[i]] = p; else rk[sa[i]] = ++p; } if (p == n) break; }
另一个优化是发现第一轮计数排序实际上没必要排,直接把上一轮得到的 sa 的后面移到前面(i+w>n),剩下的只管这个子串后半部分的排名实际上就是上一轮的排名。但你可以发现这个至多只能优化到原来的 7/9,而且写这个的时候第一轮开始前的 sa 必须已经是排好序的:
#include<bits/stdc++.h> usingnamespace std; constexprint N = 1e6 + 5;
int n; char a[N]; /** * @brief rk[i] 表示后缀 i 的排名 * @brief sa[i] 表示排名为 i 的后缀 */ int rk[N], sa[N], ork[N], id[N], cnt[N]; inlineintgrk(int x){ return x <= n ? rk[x] : 0; } inlineintgork(int x){ return x <= n ? ork[x] : 0; }
intmain(void){ ios::sync_with_stdio(false); cin.tie(nullptr); // cin >> n >> a; cin >> a; n = strlen(a); for (int i = n; i >= 1; --i) a[i] = a[i - 1];
int p = 62; for (int i = 1; i <= n; ++i) sa[i] = i, rk[i] = (isdigit(a[i]) ? a[i] - '0' + 1 : ('A' <= a[i] && a[i] <= 'Z' ? a[i] - 'A' + 11 : a[i] - 'a' + 37)); for (int w = 1; w < n; w <<= 1) { // 现在需要求得 w * 2 时的 SA // (rk[x], rk[x + w]) // sort(sa + 1, sa + n + 1, [&](int x, int y) { return rk[x] == rk[y] ? grk(x + w) < grk(y + w) : rk[x] < rk[y]; }); if (w > 1) { // 已知的 sa 是 w 时的 sa int cur = 0; for (int i = n - w + 1; i <= n; ++i) id[++cur] = i; for (int i = 1; i <= n; ++i) if (sa[i] > w) id[++cur] = sa[i] - w; } else { for (int i = 0; i <= p + 1; ++i) cnt[i] = 0; for (int i = 1; i <= n; ++i) id[i] = sa[i], ++cnt[grk(i + w) + 1]; for (int i = 1; i <= p + 1; ++i) cnt[i] += cnt[i - 1]; for (int i = 1; i <= n; ++i) sa[++cnt[grk(id[i] + w)]] = id[i]; for (int i = 1; i <= n; ++i) id[i] = sa[i]; }
for (int i = 0; i <= p; ++i) cnt[i] = 0; for (int i = 1; i <= n; ++i) ++cnt[rk[i]]; for (int i = 1; i <= p; ++i) cnt[i] += cnt[i - 1]; for (int i = 1; i <= n; ++i) sa[++cnt[rk[id[i]] - 1]] = id[i];
swap(ork, rk); p = 0; for (int i = 1; i <= n; ++i) { if (i && ork[sa[i]] == ork[sa[i - 1]] && gork(sa[i] + w) == gork(sa[i - 1] + w)) rk[sa[i]] = p; else rk[sa[i]] = ++p; } if (p == n) break; } for (int i = 1; i <= n; ++i) cout << sa[i] << " \n"[i == n]; return0; }
1.2 SA-IS
1.3 height
定义 hti=∣LCP(sai−1,sai)∣。你当然可以求出 SA 之后二分 + 哈希获得 height 数组,但这成求任意子串的 LCP 了,完全没管 sa 本身的定义。