ST 表学习笔记

目录

  1. 什么是 ST 表

  2. 建造 ST 表

  3. ST 表查询操作

  4. ST 表的适用范围与数据结构的对比

  5. 模板题

  6. 结语&参考文献

什么是 ST 表

ST 表,即 Sprase Table,也叫稀疏表。
它主要用于解决 RMQ(Range Minimum/Maximum Query,区间最值查询)问题。

更生动地讲,ST 表就是在 \(O(n \log n)\) 的时间里预处理个表,然后可以在 \(O(1)\) 的时间内查询一个区间的最大值或最小值。

ST 表本质上是动态规划

建造 ST 表

与线段树、树状数组一样,ST 表要先建造。

假设我们需要求区间最小值。

\(dp_{i, j}\) 表示以 \(i\) 为头,长度为 \(2^j\) 的区间的最小值。

例如,\(dp_{5, 2}\) 表示 \([5, 8]\) 的元素的最小值。

int dp[数组大小 N][log(N)+5];

\(N = 10^5\),就可以定义成: int minn[100005][20]


首先初始化,令 \(dp_{i, 0} = a_i\) 即可。

for (int i = 1; i <= n; i++) minn[i][0] = read();

* 程序中为了区分是求最大值还是最小值,将 dp 改成了 minn。不过终究是个名字罢了,不用在意。

状态转移方程很简单:\(dp_{i, j} = \min(dp[i][j-1], dp[i+2^{j-1}][j-1])\)

原因更简单,长度为 \(2^j\) 的最值就是两段长度为 \(2^{j-1}\) 合起来的最值。

外层 for 循环:先枚举长度 \(len\),即 \(2^1\)\(2^{\left\lfloor\log n\right\rfloor}\)

内层 for 循环:枚举起点,即 \(1\)\((n - 2^{len} + 1)\)

代码:

void build()
{
    for (int i = 1; i <= n; i++) minn[i][0] = read();
    int k = log2(n);
    for (int j = 1; j <= k; j++)
    {
        int R = n - (1 << j) + 1;
        for (int i = 1; i <= R; i++) minn[i][j] = min(minn[i][j-1], minn[i + (1 << (j-1))][j-1]);
    }
}

* 代码中的 log2()cmath 库中的函数。

ST 表查询操作

前面已经提到,给定 \(L\)\(R\),求 \(\min\limits_{R}^{i=L} a_i\) 的值。

\(k = \left\lfloor\log(R-L+1)\right\rfloor\),其中 \((R-L+1)\) 为区间长度。

这张图很清晰地讲解了 ST 表的查询操作。

那么代码实现就非常容易了。

int query(int L, int R)
{
	int k = log2(R-L+1);
	return min(minn[L][k], minn[R - (1<<k) + 1][k]);
}

ST 表的适用范围与数据结构的对比

在这章开始之前,先扔个完整 ST 表代码:

//结构体外的定义
#define N 100005
int n;
//结构体
struct ST
{
    int minn[N][20];
	void build()
	{
	    for (int i = 1; i <= n; i++) minn[i][0] = read();
	    int k = log2(n);
	    for (int j = 1; j <= k; j++)
	    {
	        int R = n - (1 << j) + 1;
	        for (int i = 1; i <= R; i++) minn[i][j] = min(minn[i][j-1], minn[i + (1 << (j-1))][j-1]);
	    }
	}
	int query(int L, int R)
	{
		int k = log2(R-L+1);
		return min(minn[L][k], minn[R - (1<<k) + 1][k]);
	}
};
ST a;

用结构体封装了,代码很清新吧!

大家观察一下代码,不难发现:

  • build() 的时间复杂度是 \(O(n \log n)\)

  • query() 的时间复杂度是 \(O(1)\)

我们发现,线段树同样可以解决这类问题。

将线段树拉过来对比一下:

* 前台图炸了,只能在后台截图QwQ

通过表格可以看到,两者各有利弊。

模板题

T1:P1816

求最小。

直接用上文的代码即可。

T2:P3865

求最大。

代码稍微更改一下就好。

T3:P2880

同时维护最大与最小的 ST 表。

查询时作差即可,非常简单。

代码略。

* 如果你真的想学习 ST 表,以上模板题建议从零开始盲打一遍。

结语 & 参考文献

感谢大家看完整篇文章。

大部分内容参考了书籍《算法训练营:进阶篇》。

图片均为自己手绘,图片与文章未经允许不得转载。

首发:2022-05-07 16:35:56

posted @ 2022-08-26 01:50  liangbowen  阅读(169)  评论(0编辑  收藏  举报