You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2.8 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

https://blog.csdn.net/CUCUC1/article/details/113706736

最长公共上升子序列(LCIS)

### 前置知识

LCS

\LARGE f[i][j]= \left\{\begin{matrix}
 f[i-1][j-1]+1& i,j>0,a[i]=b[j] \\
 max(f[i,j-1],f[i-1][j])& i,j>0,a[i]\neq b[j]
\end{matrix}\right.

LIS

自行脑补


注意: 刚开始看这个问题的时候,第一反应是先求出LCS再求出LCSLIS,事实上这是有问题的,我们并不能保证这么求出的LCIS是最长的,比如下面这个例子:

Example a:[7 1 5 6 4 2 7] b:[7 1 5 4 6 7 2] 按照递归的取最长公共子序列,取出: [7 1 5 6 2] 此序列的最长上升子序列为: 1 5 6 (len=3) 但原序列的“最长公共上升子序列”为: 1 5 6 7 (len=4)

小结: 证明一个假设是错的很简单,直接找一个反例就行了;证明一个假设是正确的很难,因为你要证明所有情况都是对的。

这道题目是AcWing 895. 最长上升子序列和AcWing 897. 最长公共子序列的结合版,在状态表示和状态计算上都是融合了这两道题目的方法。

状态表示:

  • f[i][j]代表所有a[1 ~ i]b[1 ~ j]中以b[j]结尾的公共上升子序列的集合;

  • f[i][j]的值等于该集合的子序列中长度的最大值;

状态计算(对应集合划分):

首先依据公共子序列中是否包含a[i],将f[i][j]所代表的集合划分成两个不重不漏的子集:

  • 不包含a[i]的子集,最大值是f[i - 1][j]
  • 包含a[i]的子集,将这个子集继续划分,依据是子序列的倒数第二个元素在b[]中是哪个数:
    • 子序列只包含b[j]一个数,长度是1
    • 子序列的倒数第二个数是b[1]的集合,最大长度是f[i - 1][1] + 1
    • 子序列的倒数第二个数是b[j - 1]的集合,最大长度是f[i - 1][j - 1] + 1

如果直接按上述思路实现,需要三重循环:

for (int i = 1; i <= n; i ++ ){
    for (int j = 1; j <= n; j ++ ){
        f[i][j] = f[i - 1][j];
        if (a[i] == b[j]){
            int maxv = 1;
            for (int k = 1; k < j; k ++ )
                if (a[i] > b[k])
                    maxv = max(maxv, f[i - 1][k] + 1);
            f[i][j] = max(f[i][j], maxv);
        }
    }
}

然后我们发现每次循环求得的maxv是满足a[i] > b[k]f[i - 1][k] + 1的前缀最大值。 因此可以直接将maxv提到第一层循环外面,减少重复计算,此时只剩下两重循环。

最终答案枚举子序列结尾取最大值即可。

时间复杂度 代码中一共两重循环,因此时间复杂度是 O(n2)