一、ST算法(Sparse Table):
- ST(Sparse Table,稀疏表)算法是求解RMQ问题的经典在线算法,以O(nlogn)时间预处理,然后在O(1)时间内回答每个查询。ST算法本质上是动态规划算法,定义了一个二维辅助数组st[n][n],st[i][j]表示原数组a中从下标i开始,长度为2^j的子数组中的最值(以最小值为例)。
- 预处理:要求解st[i][j]时,即求下标i开始,长度为2^j的子数组的最小值时,可以把这段子数组再划分成两半,每半的长度为2^(j-1),于是前一半的最小值为st[i][j-1],后一半的最小值为st[i+2^(j-1)][j-1],于是动态规划的转移方程为:st[i][j] = min(st[i][j-1], st[i+2^(j-1)][j-1])长度为2^j的情况只和长度为2^(j-1)的情况有关,只需要初始化长度为2^0=1的情况即可。而长度为1时的最小值是显然的(为其本身)。
-
查询: 现在问题是,st数组可以怎样加速我们的查询呢?这也是算法的巧妙之处,假设求下标在u到v之间的最小值。先求u和v之间的长度len=v-u+1,然后求k=ln(len),则u到v之间的子数组可以分为两部分: 以u开始,长度为2^k的一段 以v结束,长度为2^k的一段(可以计算得到起始位置为v-2^k+1)注意,一般情况下这两段是重叠的,但是这两段的最小值中较小的一个仍然是u到v的最小值。于是RMQ(u,v) = min(st[u][k], st[v-2^k+1][k])
- 原文链接:http://noalgo.info/489.html
二、关键代码:
- 预处理:
void Init(){ for(int i = 0; i < MaxSize; ++i) ST[i][0] = Source[i]; for(int j = 1; (1<<j) <= MaxSize; ++j) for(int i = 0; i + (1<<j) - 1 < MaxSize; ++i) ST[i][j] = max(ST[i][j-1],ST[i + (1<<(j-1))][j-1]); }
- 查询:
int ln(int x){//对数函数 向下取整 float fx; unsigned long ix, exp; fx = (float)x; ix = *(unsigned long*)&fx; exp = (ix >> 23) & 0xFF; return exp - 127; } int RMQ(int l,int r){ int len = r - l + 1; int k = ln(len); return max(ST[l][k],ST[l+(r - (<span id="transmark"></span>1<<k) + 1][k]); }