ST 表 (Sparse Table)¶
ST 表(Sparse Table)主要用于求解静态区间最值查询 (RMQ - Range Minimum/Maximum Query) 等满足可重复贡献(幂等性,Idempotence,即 \(x \circ x = x\))的问题。
预处理时间复杂度为 \(\mathcal{O}(n \log n)\),单次区间查询只需 \(\mathcal{O}(1)\)。
模版实现 (区间最大值)¶
0-indexed 简洁实现,预计算 \(\log_2\) 表加速查询。
#include <vector>
#include <algorithm>
#include <cmath>
template <typename T>
struct SparseTable {
int n;
int K;
std::vector<int> lg;
std::vector<std::vector<T>> st;
SparseTable(const std::vector<T> &a) {
n = a.size();
K = std::__lg(n) + 1;
lg.assign(n + 1, 0);
for (int i = 2; i <= n; ++i) {
lg[i] = lg[i >> 1] + 1;
}
st.assign(K, std::vector<T>(n));
st[0] = a;
for (int j = 1; j < K; ++j) {
for (int i = 0; i + (1 << j) <= n; ++i) {
st[j][i] = std::max(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
}
}
}
// 查询区间 [l, r] 的最大值 (0 <= l <= r < n)
T query(int l, int r) const {
int k = lg[r - l + 1];
return std::max(st[k][l], st[k][r - (1 << k) + 1]);
}
};
扩展:区间 GCD¶
因为 \(\gcd(x, x) = x\),GCD 同样满足可重复贡献特性,因此也可以使用 ST 表在 \(\mathcal{O}(1)\) 次 GCD 计算时间内回答区间最大公约数查询。
#include <numeric>
// 只需将递推与查询中的 std::max 替换为 std::gcd 即可:
// st[j][i] = std::gcd(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
// return std::gcd(st[k][l], st[k][r - (1 << k) + 1]);
LeetCode 经典真题精选与题解提示¶
-
LeetCode 239 - 滑动窗口最大值 (Sliding Window Maximum)
困难提示
静态 RMQ 与滑动窗口最值。
- 尽管本题最优解为单调队列 \(\mathcal{O}(n)\),但在数组静态无需修改时,ST 表预处理 \(\mathcal{O}(n \log n)\)、回答各区间 \([i, i + k - 1]\) 查询仅需 \(\mathcal{O}(1)\),能够稳定通过。
-
LeetCode 3171 - 找到按位与最接近 K 的子数组 (Find Subarray With Bitwise AND Closest to K)
困难提示
ST 表维护位运算 + 二分搜索。
- 按位与运算(\(\&\))满足幂等性(\(x \& x = x\))和区间可重复贡献性,并且随着子数组右端点延伸单调不增。
- 用 ST 表在 \(\mathcal{O}(n \log n)\) 时间内维护区间按位与,对于每个固定的左端点 \(i\),利用 ST 表在 \(\mathcal{O}(\log n)\) 时间内二分右端点即可找到最接近 \(k\) 的值。
-
LeetCode 2447 - 最大公因数等于 K 的子数组数目 (Number of Subarrays With GCD Equal to K)
中等提示
区间 GCD 查询。
- 子数组的 GCD 具有结合律与幂等性。构建 GCD 的 ST 表后,可以在 \(\mathcal{O}(1)\) 次 GCD 计算时间内查询任意区间 \([l, r]\) 的公约数,结合二分即可统计出每个左端点对应的合法区间。