ST表
离散表(Sparse Table,ST),主要用来解决最大值/最小值查询(Range Minimum/Maximum Query,RMQ),可以快速的查询区间内的最大值最小值。lookup[i][j]表示从下标i开始长度为
$2^j$的区间的最小值
。

区间[i,j]
长度为L=j-i+1
,得到k
使得$2k<=L并且2{k+1}>L$,考虑两个子区间$[i,i+2k-1]$和$[j-2k+1,j]$,两个区间的长度为$2k$,这两个区间重叠。则$min(i,j)=min(lookup[i][k],lookup[j-2k+1][k])$。
计算lookup
,$lookup[i][j]=min(lookup[i][j-1],lookup[i+2{j-1}][j-1])$,将长度为$2j$的区间分为长度为$2^{j-1}$的两个区间。
$$
计算lookup需要O(nlogn)的时间和空间复杂度\
查询O(1)复杂度
$$
适用于数组不变、多次查询
其实ST表不仅能处理最大值/最小值,凡是符合结合律且可重复贡献的信息查询都可以使用ST表高效进行。什么叫可重复贡献呢?设有一个二元运算$f(x,y)$,满足 $f(a,a)=a$,则$f$是可重复贡献的。显然最大值、最小值、最大公因数、最小公倍数、按位或、按位与都符合这个条件。可重复贡献的意义在于,可以对两个交集不为空的区间进行信息合并。
class Solution { public: long long continuousSubarrays(vector<int> &nums) { int n = nums.size(); int m = log2(n);
vector<vector<int>> dpMin(n, vector<int>(m + 1)), dpMax(n, vector<int>(m + 1)); for (int i = 0; i < n; i++) { dpMin[i][0] = nums[i]; dpMax[i][0] = nums[i]; }
for (int j = 1; (1 << j) <= n; j++) { for (int i = 0; i + (1 << j) <= n; i++) { dpMin[i][j] = min(dpMin[i][j - 1], dpMin[i + (1 << (j - 1))][j - 1]); dpMax[i][j] = max(dpMax[i][j - 1], dpMax[i + (1 << (j - 1))][j - 1]); } }
long long ans = 0; int left = 0;
for (int right = 0; right < n; right++) { int k = log2(right - left + 1); int mn = min(dpMin[left][k], dpMin[right - (1 << k) + 1][k]); int mx = max(dpMax[left][k], dpMax[right - (1 << k) + 1][k]); while (mx - mn > 2) { left++; k = log2(right - left + 1); mn = min(dpMin[left][k], dpMin[right - (1 << k) + 1][k]); mx = max(dpMax[left][k], dpMax[right - (1 << k) + 1][k]); } ans += (right - left + 1); } return ans; } };
|
参考
数组连续区间的最大最小值查询 - 简书 (jianshu.com)
算法学习笔记(12): ST表 - 知乎 (zhihu.com)
思维提升|leetcode]6种算法解决LeetCode困难题:滑动窗口最大值_leetcode st表_ErikTse_的博客-CSDN博客
ST表算法详解 - soul_maker - 博客园 (cnblogs.com)
(78条消息) ST表详解(稀疏表)_C+G的博客-CSDN博客(题目)