返回

动态规划:破解最长公共子序列

见解分享

踏入算法世界:用动态规划求解最长公共子序列(LCS)

引言

在浩瀚的算法世界中,动态规划技术如同一把利剑,所向披靡。它以其庖丁解牛般的分解能力和逐层求解的巧妙思想著称。今天,我们将聚焦于最长公共子序列(LCS)问题,领略动态规划的非凡魅力。

LCS 的奥秘

与最长公共子串问题不同,LCS 要求子序列中的元素按照相同顺序排列,而无需连续出现。例如,对于序列 "abcde" 和 "abefd",LCS 为 "abd",而最长公共子串为 "ab"。

动态规划的魔力

动态规划的核心思想在于将复杂问题化繁为简,将其分解为一系列较小的子问题,再利用已解决的子问题的解逐层解决整个问题。对于 LCS 来说,我们可以将问题分解为:

  1. 找出两个序列的最后一个元素是否相同。
  2. 如果相同,则 LCS 的最后一个元素也是它。
  3. 如果不同,则递归地求解两个序列的前缀(删除最后一个元素)的 LCS。

Python 实现

为了加深理解,让我们编写一个 Python 函数来求解 LCS:

def LCS(seq1, seq2):
    len1 = len(seq1)
    len2 = len(seq2)
    # 创建动态规划表格
    table = [[0 for _ in range(len2 + 1)] for _ in range(len1 + 1)]
    
    # 填充表格
    for i in range(1, len1 + 1):
        for j in range(1, len2 + 1):
            if seq1[i - 1] == seq2[j - 1]:
                table[i][j] = table[i - 1][j - 1] + 1
            else:
                table[i][j] = max(table[i][j - 1], table[i - 1][j])
    
    # 回溯构建 LCS
    lcs = ""
    i = len1
    j = len2
    while i > 0 and j > 0:
        if seq1[i - 1] == seq2[j - 1]:
            lcs = seq1[i - 1] + lcs
            i -= 1
            j -= 1
        else:
            if table[i][j - 1] > table[i - 1][j]:
                j -= 1
            else:
                i -= 1
    
    return lcs

细细品味,体会精髓

动态规划的精髓在于利用递推关系构建表格,存储已解决子问题的解。通过逐层累加这些解,我们最终可以解决整个问题。

在 LCS 的例子中,动态规划表格的每一格都代表了两个序列的前缀的 LCS 长度。通过巧妙地利用表格中的信息,我们可以高效地回溯构建最终的 LCS。

展望未来,探索更多

动态规划是一把算法界的利剑,在众多问题中大显身手,如最长公共子字符串、最长递增子序列、编辑距离等。掌握动态规划的思想,您将在算法世界中如虎添翼。

常见问题解答

1. 什么是最长公共子序列(LCS)?

LCS 是两个序列中元素按照相同顺序排列的最长子序列,无需连续出现。

2. 动态规划如何解决 LCS 问题?

动态规划将 LCS 问题分解为子问题,利用递推关系构建表格,存储已解决的子问题的解,逐层累加这些解求解整个问题。

3. 代码示例中的 table 数组有什么作用?

table 数组存储了两个序列的前缀的 LCS 长度,每一格代表一个子问题的解。

4. 如何使用动态规划解决其他问题?

掌握动态规划的思想和步骤,可以解决一系列复杂问题,例如最长公共子字符串、最长递增子序列等。

5. 动态规划的优势是什么?

动态规划的优势在于能够将复杂问题分解为较小的子问题,并利用已解决的子问题的解逐步解决整个问题,提高效率和减少重复计算。