📜  门| GATE CS Mock 2018 |设置 2 |第 46 题(1)

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

门 | GATE CS Mock 2018 | 设置 2 | 第 46 题

本题目为 Gate CS Mock 2018 的第 46 题,属于计算机科学领域,需要解决的问题为如何实现线性时间求解最长公共子序列(LCS)问题。

问题描述

给定两个字符串 S1 和 S2,写一个线性时间算法来查找它们之间的最长公共子序列(LCS)。

解题思路

本题要求线性时间复杂度的解法,需要结合动态规划思想来求解最长公共子序列(LCS)问题。

我们可以建立一个二维数组 dp,其中 dp [ i ][ j ] 表示 S1[0:i] 和 S2[0:j] 之间的 LCS 长度。那么我们可以得到如下的递推式:

if s1[i] == s2[j]:
    dp[i][j] = dp[i-1][j-1] + 1
else:
    dp[i][j] = max(dp[i-1][j], dp[i][j-1])

其中,如果 s1[i] == s2[j],表示当前位置匹配,那么 dp[i][j] 应该等于 dp[i-1][j-1] + 1;如果不匹配,那么 dp[i][j] 应该等于 dp[i-1][j] 和 dp[i][j-1] 中的较大值。

最终 DP 表格的右下角就是两个字符串的最长公共子序列的长度。

而最长公共子序列本身可以通过 DP 表格进行回溯获得,具体步骤为:

  1. 从 DP 表格的右下角开始,判断当前位置的字符是否相等。

  2. 如果相等,则说明这个字符是最长公共子序列中的一个字符,记录下来。

  3. 根据 DP 表格的递推式,有以下几种情况:

    • 如果 dp[i-1][j] == dp[i][j],说明当前字符在 S1 中没有匹配到,需要向上回溯;

    • 如果 dp[i][j-1] == dp[i][j],说明当前字符在 S2 中没有匹配到,需要向左回溯;

    • 如果既不满足上面两个条件,说明当前字符既可以向上回溯,也可以向左回溯,可以任选其一。

  4. 重复步骤 1-3,直至回溯到 DP 表格的左上角。

可以看出,由于回溯的过程中涉及到多次查表和判断字符相等的操作,因此需要 O(m+n) 的时间复杂度。

代码实现

以下是完整的 Python 代码实现:

def lcs(s1, s2):
    # 创建 DP 表格
    dp = [[0] * (len(s2) + 1) for _ in range(len(s1) + 1)]

    # 更新 DP 表格
    for i in range(1, len(s1) + 1):
        for j in range(1, len(s2) + 1):
            if s1[i-1] == s2[j-1]:  # 匹配
                dp[i][j] = dp[i-1][j-1] + 1
            else:  # 不匹配
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])

    # 回溯解法
    i, j = len(s1), len(s2)
    lcs_str = ''
    while i > 0 and j > 0:
        if s1[i-1] == s2[j-1]:
            lcs_str += s1[i-1]
            i, j = i-1, j-1
        elif dp[i-1][j] >= dp[i][j-1]:
            i -= 1
        else:
            j -= 1
    
    # 倒序输出最长公共子序列
    return lcs_str[::-1]

至此,我们便完成了本题的代码实现。

参考资料
  1. 最长公共子序列(LCS),GeeksforGeeks.

  2. 最长公共子序列(动态规划),阿靖~要做个好前端.

  3. LintCode 77 最长公共子序列,胡云迪.