LCIS 最长公共上升子序列学习笔记

什么是 LCIS

LCIS 是建立在 LIS 与 LCS 的基础之上的问题。需要我们求解不同序列的公共子序列中的最长上升子序列等问题。

例子

假设我们拥有以下两个序列:

a:2 2 1 3b:2 1 2 3a : 2 \ 2 \ 1 \ 3 \\ b : 2 \ 1 \ 2 \ 3

那么这两个序列的最长公共上升子序列就是 1 31 \ 3,长度为 22

解决方法

状态设计

习惯性写下了状态的表示:

dpi,j 表示 a1i 与 b1j 的 LCIS 的长度 dp_{i, j} \text { 表示 } a_{1 \sim i} \text { 与 } b_{1 \sim j} \text { 的 LCIS 的长度 }

初始化

由于当子序列长度为 11 时并不存在 LCIS,所以进行如下初始化:

dp1,1=dp_{1, 1} = \infty

转移

  • aibja_{i} \neq b_{j} 时,由于我们外层循环为 ii,所以此时 dpi,j=dpi1,jdp_{i, j} = dp_{i - 1, j}
  • ai=bja_{i} = b_{j} 时,我们需要选出一个 1k<j1 \leq k < jbk<aib_{k} < a_{i} 使得 dpi,kdp_{i, k} 最大,则此时这个最大值为

    dpi,j=max2k<jbk<ai(dpi1,k)+1dp_{i, j} = \max \limits_{2 \leq k < j \land b_{k} < a_{i}}(dp_{i - 1, k}) + 1

综上,转移如下:

dpi,j={dpi1,j,aibimax2k<jbk<ai(dpi1,k)+1,otherwisedp_{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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for (int i = 2; i <= n; ++i) {
for (int j = 2; j <= n; ++j) {
if (a[i] != b[j]) {
dp[i][j] = dp[i - 1][j];
} else {
int maxVal = 0;
for (int k = 2; k < j; ++k) {
if (b[k] < a[i]) {
maxVal = max(maxVal, dp[i - 1][k]);
}
}
dp[i][j] = maxVal + 1;
}
}
}

但是这样的时间复杂度为 O(n3)O(n^{3}),能不能对其进行优化呢?

我们发现当计算到 ai=bja_{i} = b_{j} 时需要使用第三层循环来寻找 kk,那么有没有一种方法能够快速寻找 max2k<jbk<ai(dpi1,k)\max \limits_{2 \leq k < j \land b_{k} < a_{i}}(dp_{i - 1, k})

基于以上想法,我们拥有两种常见思路:

  • 构建 ST 表来做 RMQ,但对于每一个 ii 都需要重新构建一次,复杂度为 O(nlog2n)O(n \log_{2}n),比原先的暴力循环更慢
  • 滑动窗口(单调队列)求最大值,同样对于每一个 ii 都需要重新计算区间最值,同时区间长度不定,与暴力循环无区别

以上常见的方法都不行,那我们从转移本身进行思考,对于每一对 i,ji, j,当内层对 jj 循环时外层的 ii 保持不变,则我们在循环 ii 时记录一个变量 maxVal 表示 max2k<jbk<ai(dpi1,k)\max \limits_{2 \leq k < j \land b_{k} < a_{i}}(dp_{i - 1, k}),当 jj 增加 11 时对新的 bjb_{j} 进行判断是否需要更新 maxVal 即可。时间复杂度为 O(1)O(1)

优化后的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int maxVal = 0;
for (int i = 2; i <= n; ++i) {
maxVal = 0;
if (a[i] > b[1]) {
maxVal = dp[i - 1][1];
}
for (int j = 2; j <= n; ++j) {
if (a[i] == b[j]) {
dp[i][j] = maxVal + 1;
} else {
dp[i][j] = dp[i - 1][j];
}
if (a[i] > b[j]) {
maxVal = max(maxVal, dp[i - 1][j]);
}
}
}

时间复杂度

综上,整体时间复杂度为 O(n2)O(n^{2}),1s 内 nn 的最大值约为 10410^{4}

类似练习

  • AcWing 272. 最长公共上升子序列