📌  相关文章
📜  两个数组的最长公共子序列,其中一个数组仅由不同的元素组成(1)

📅  最后修改于: 2023-12-03 15:35:56.703000             🧑  作者: Mango

两个数组的最长公共子序列

在计算机科学中,最长公共子序列(Longest Common Subsequence,简称LCS)指两个序列最长的公共子序列,不要求各个元素在原序列中的位置相同,但是在新的序列中,这些元素的相对位置必须相同。这个问题是计算机科学中常见的一个问题,也是动态规划的经典问题之一。本文会介绍如何解决两个数组的最长公共子序列问题,其中一个数组仅由不同的元素组成。

问题描述

给定两个长度为 $m$ 和 $n$ 的数组 $A$ 和 $B$,其中 $A$ 数组仅由不同的元素组成。题目要求在 $B$ 数组中找到一个最长的子序列 $C$,使得 $C$ 中的元素在 $A$ 数组中也存在,并且 $C$ 在 $B$ 数组中的顺序不变。返回 $C$ 的长度。

解决思路

首先,我们需要明确一个概念——子序列。在本问题中,子序列指的是原序列中删除任意个数后得到的序列。例如,对于序列 $A = {1, 2, 3, 4, 5}$,它的一个子序列可以是 ${1, 3, 4}$。特别地,当删除所有的数后,得到的序列称为空序列,长度为 $0$。

接下来,我们可以使用动态规划的思想来解决这个问题。我们定义一个二维数组 $dp$,其中 $dp_{i,j}$ 表示 $A$ 数组中前 $i$ 个元素和 $B$ 数组前 $j$ 个元素的 LCS 的长度。

显而易见的,当 $i=0$ 或 $j=0$ 时,$dp_{i,j}$ 的值为 $0$,因为此时一个数组中都没有元素,其 LCS 的长度必然为 $0$。

当 $i>0$ 且 $j>0$ 时,我们需要考虑两种情况:$A_i$ 不在子序列中和 $A_i$ 在子序列中。如果 $A_i$ 不在子序列中,那么目标子序列就是 $A_{1\sim (i-1)}$ 和 $B_{1\sim j}$ 的 LCS,因此 $dp_{i,j} = dp_{i-1,j}$;如果 $A_i$ 在子序列中,那么它肯定和 $B$ 数组中的某个数(因为 $B$ 数组中都是不同的元素,因此至少有一个数和 $A_i$ 相同)组成目标子序列的最后一个元素,而这个数对应的下标在 $B$ 数组中一定 $\le j$,因此目标子序列一定在 $A_{1\sim (i-1)}$ 和 $B_{1\sim (j-1)}$ 的 LCS 基础上,添加 $B_j$ 或者不变。综上所述,状态转移方程为:

$$ dp_{i,j} = \begin{cases} 0 \quad \quad \quad \quad \quad \quad \quad \ \ \ \qquad(i=0 \text{ or } j=0) \ dp_{i-1,j} \qquad \qquad \qquad (A_i \text{ 不在子序列中}) \ dp_{i-1,j-1}+1 \quad (A_i \text{ 在子序列中,且 } B_j = A_i) \ dp_{i,j-1} \quad \quad \quad \quad \quad(A_i \text{ 在子序列中,但 } B_j \neq A_i) \ \end{cases} $$

最终,$dp_{m,n}$ 就是 $A$ 数组和 $B$ 数组的最长公共子序列的长度。

代码实现

下面是该问题的 Python3 代码实现,时间复杂度为 $O(mn)$:

def longestUniqCommonSubsequence(A, B):
    m, n = len(A), len(B)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if A[i - 1] != B[j - 1]:
                dp[i][j] = dp[i][j - 1]
            else:
                dp[i][j] = dp[i - 1][j - 1] + 1
    return dp[m][n]

此处省略了对输入参数的类型和大小的校验,实际情况中应当对其进行判断。

总结

本文介绍了如何解决两个数组的最长公共子序列问题,其中一个数组仅由不同的元素组成。通过使用动态规划的思想,可以实现时间复杂度为 $O(mn)$ 的高效算法。