📜  打印最长的公共子序列设置2(全部打印)(1)

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

打印最长的公共子序列设置2(全部打印)

在字符串处理中,最长公共子序列 (LCS) 是一道经典的问题。给定两个字符串,求它们的最长公共子序列。这个问题具有广泛的应用,比如文本相似度计算、基因序列分析等。然而,当我们得出最长公共子序列的长度时,有时候我们还需要知道这个最长公共子序列的具体内容。本文将介绍如何输出最长公共子序列的内容。

暴力枚举法

暴力枚举法的思路非常简单:枚举出所有的子序列,然后依次判断是否为公共子序列。在枚举所有的子序列时,我们可以使用回溯法来求出所有的子序列。如果一个子序列是两个字符串的公共子序列,则通过与当前得到的最长公共子序列的长度比较,更新最长公共子序列。暴力枚举法虽然思路简单,但是时间复杂度为 $O(2^n)$,在实际应用中无法满足要求。

动态规划法

动态规划法是解决最长公共子序列问题的最优解法。其思路是基于子问题的最优性原理,将原问题划分成若干规模较小的子问题,然后将子问题的最优解逐步合成原问题的最优解。当我们得出最长公共子序列的长度时,我们也就可以得到最长公共子序列。下面是动态规划法的主要步骤:

  1. 定义状态:设 $f[i][j]$ 为 $A[1:i]$ 和 $B[1:j]$ 的最长公共子序列的长度,则原问题为 $f[m][n]$,其中 $m$ 和 $n$ 分别为字符串 $A$ 和 $B$ 的长度。
  2. 初始化:显然 $f[i][0]=f[0][j]=0$。这是因为,当一个字符串的长度为 0 时,与另一个字符串的最长公共子序列的长度显然为 0。
  3. 转移方程:设 $A[i]$ 是字符串 $A$ 的第 $i$ 个字符,$B[j]$ 是字符串 $B$ 的第 $j$ 个字符。则根据当前字符是否相同,我们分为两种情况:若 $A[i]=B[j]$,则 $f[i][j]=f[i-1][j-1]+1$;若 $A[i]≠B[j]$,则 $f[i][j]=\max(f[i-1][j],f[i][j-1])$。
  4. 输出最长公共子序列内容:在计算出 $f[m][n]$ 的同时,我们可以用类似于动态规划的思想,记录下两个字符串中的最长公共子序列的内容。从 $f[m][n]$ 开始,如果 $A[m]=B[n]$,则当前字符一定属于最长公共子序列。将该字符加入结果中,并在 $f[m-1][n-1]$ 处重复该过程。否则,若 $f[m-1][n]>f[m][n-1]$,则说明当前字符不属于最长公共子序列,我们需要将位置 $m$ 置为 $m-1$,继续重复该过程,直到 $A[m]=B[n]$。

下面是 Python 代码,实现了动态规划法,并输出了最长公共子序列的内容。注意:此代码返回的是最长公共子序列的内容,而不仅仅是长度。

def lcs(A, B):
    m, n = len(A), len(B)
    f = [[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]:
                f[i][j] = f[i-1][j-1] + 1
            else:
                f[i][j] = max(f[i-1][j], f[i][j-1])
    res = ""
    while m>0 and n>0:
        if A[m-1] == B[n-1]:
            res = A[m-1] + res
            m -= 1
            n -= 1
        elif f[m-1][n] > f[m][n-1]:
            m -= 1
        else:
            n -= 1
    return res

上述代码的时间复杂度为 $O(mn)$,适用于大多数实际应用场景。

总结

本文介绍了如何输出最长公共子序列的内容。我们讲解了暴力枚举法和动态规划法两种解法,并给出了 Python 代码实现。在实际应用中,动态规划法是解决最长公共子序列问题的最优解法,其时间复杂度为 $O(mn)$。