LCIS 最长上升公共子序列
前言
LCIS 指的是一个两个序列中最长的公共子序列,且这个子序列需要满足单调递增的这个性质。但是目前据我所知并不存在 LCIS 的 nlogn 算法,因此在这里将介绍 LCIS 的 n ^ 2 算法
算法分析
首先我们需要确定出 f 数组的定义,在这里我定义 f[i][j] 表示的是 a 数组的前 i 个数, b 数组前 j 个数并以 b[j] 结尾的最长上升公共子序列的长度
明确出 f 数组的定义之后,类比 LCS 的 n ^ 2 做法的状态转移方程,就可以推出状态转移方程了:
当 a[i] != b[j] 时: f[i][j] = f[i - 1][j]
当 a[i] == b[j] 时:f[i][j] = max(f[i - p][k]) + 1 (p ≤ k ≤ j - 1 且 b[j] > b[k])
这个状态转移方程的正确性是显而易见的,但是如果朴素实现这个状态转移方程的时间复杂度是 O(n ^ 3)。而实现我们所期待的 O(n ^ 2),则需要按照一个合理的递推顺序。
外层循环枚举 i ,内层循环枚举 j 。a[i] == b[j] 的情况中有一个条件是 b[j] > b[k],由于 b[j] == a[i],这个条件可以改写成 a[i] > b[k]。内层循环 j 是由小至大枚举的,因此我们可以在枚举 j 的同时计算出对应的 k 。当 k 固定时, f[m][k] 是一个单调不递减的序列,那么 i - p 便无需枚举,因为 i - p 取最大值的时候 f[i - p][k] 一定取最大值
综上所述,我们可以在内层循环中维护一个 MAX 值,这个 MAX 的值是 f[i - 1][k] 的最大值且满足 a[i] > b[k] 。维护的方法非常简单,当 a[i] > b[j] 时令 MAX = max(MAX, f[i - 1][j]) 即可。由于我们维护了这个 MAX,当遇到 b[j] == a[i] 的情况时直接赋值为 MAX + 1即可。
答案为 max(f[len_a][i]) (1 ≤ i ≤ len_b)
代码
for (int i = 1; i <= a_len; i++) {
int MAX = 0;
for (int j = 1; j <= b_len; j++) {
if (a[i] > b[j])
MAX = max(MAX, f[i - 1][j]);
if (a[i] == b[j])
f[i][j] = MAX + 1;
else
f[i][j] = f[i - 1][j];
}
}
空间压缩
我们可以发现当 a[i] != b[j] 时,f[x][j] = f[x - 1][j],这说明此时我们无需改变,沿用上层数据即可,仅当 a[i] == b[j] 时令 f[j] = MAX + 1 即可。可以发现这个算法并没有优化任何的时间复杂度,仍然是 O(n ^ 2)
代码
for (int i = 1; i <= a_len; i++) {
int MAX = 0;
for (int j = 1; j <= b_len; j++) {
if (a[i] > b[j])
MAX = max(MAX, f[j]);
if (a[i] == b[j])
f[j] = MAX + 1;
}
}