LCIS 最长公共上升子序列学习笔记
什么是 LCIS
LCIS 是建立在 LIS 与 LCS 的基础之上的问题。需要我们求解不同序列的公共子序列中的最长上升子序列等问题。
例子
假设我们拥有以下两个序列:
$$
a : 2 \ 2 \ 1 \ 3 \
b : 2 \ 1 \ 2 \ 3
$$
那么这两个序列的最长公共上升子序列就是 $1 \ 3$,长度为 $2$
解决方法
状态设计
习惯性写下了状态的表示:
$$
dp_{i, j} \text { 表示 } a_{1 \sim i} \text { 与 } b_{1 \sim j} \text { 的 LCIS 的长度 }
$$
初始化
由于当子序列长度为 $1$ 时并不存在 LCIS,所以进行如下初始化:
$$
dp_{1, 1} = \infty
$$
转移
- 当 $a_{i} \neq b_{j}$ 时,由于我们外层循环为 $i$,所以此时 $dp_{i, j} = dp_{i - 1, j}$
- 当 $a_{i} = b_{j}$ 时,我们需要选出一个 $1 \leq k < j$ 且 $b_{k} < a_{i}$ 使得 $dp_{i, k}$ 最大,则此时这个最大值为
$$
dp_{i, j} = \max \limits_{2 \leq k < j \land b_{k} < a_{i}}(dp_{i - 1, k}) + 1
$$
综上,转移如下:
$$
dp_{i, j} =
\begin{cases}
dp_{i - 1, j}, & a_{i} \neq b_{i} \
\max \limits_{2 \leq k < j \land b_{k} < a_{i}}(dp_{i - 1, k}) + 1, &\text{otherwise}
\end{cases}
$$
此时就可以使用三重循环来计算
1 | for (int i = 2; i <= n; ++i) { |
但是这样的时间复杂度为 $O (n^{3})$,能不能对其进行优化呢?
我们发现当计算到 $a_{i} = b_{j}$ 时需要使用第三层循环来寻找 $k$,那么有没有一种方法能够快速寻找 $\max \limits_{2 \leq k < j \land b_{k} < a_{i}}(dp_{i - 1, k})$。
基于以上想法,我们拥有两种常见思路:
- 构建 ST 表来做 RMQ,但对于每一个 $i$ 都需要重新构建一次,复杂度为 $O (n \log_{2} n)$,比原先的暴力循环更慢
- 滑动窗口(单调队列)求最大值,同样对于每一个 $i$ 都需要重新计算区间最值,同时区间长度不定,与暴力循环无区别
以上常见的方法都不行,那我们从转移本身进行思考,对于每一对 $i, j$,当内层对 $j$ 循环时外层的 $i$ 保持不变,则我们在循环 $i$ 时记录一个变量 maxVal
表示 $\max \limits_{2 \leq k < j \land b_{k} < a_{i}}(dp_{i - 1, k})$,当 $j$ 增加 $1$ 时对新的 $b_{j}$ 进行判断是否需要更新 maxVal
即可。时间复杂度为 $O (1)$。
优化后的代码
1 | int maxVal = 0; |
时间复杂度
综上,整体时间复杂度为 $O (n^{2})$,1s 内 $n$ 的最大值约为 $10^{4}$
类似练习
- AcWing 272. 最长公共上升子序列