LCIS 最长上升公共子序列

来源:互联网 发布:mysql 命令行导入 编辑:程序博客网 时间:2024/05/16 07:20

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;    }}