📜  最长的公共子序列(1)

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

最长的公共子序列

最长的公共子序列(Longest Common Subsequence, LCS)是一道经典的算法问题。给定两个字符串s1和s2,求它们的最长公共子序列。

概念解释

先来解释一下什么是子序列。一个字符串的子序列是指该字符串通过删减某些字符(包括零个或全部)后得到的新字符串,例如字符串"abc"的子序列有"","a","b","c","ab","ac","bc","abc"。

下面再定义公共子序列。给定两个字符串s1和s2,一个字符串t是它们的公共子序列,如果t既是s1的子序列,又是s2的子序列,那么t就是s1和s2的公共子序列。

最长的公共子序列就是s1和s2的长度最长的公共子序列。

算法实现

LCS问题可以使用递归和动态规划两种算法来求解。

递归算法

递归算法的思路很简单,就是分别比较s1和s2的末尾字符是否相等,并且判断s1和s2去掉末尾字符的子字符串的LCS,再取两个LCS中长度更长的一个。递归算法的时间复杂度为O(2^n),n为s1和s2的长度之和。

def lcs(s1, s2):
    if not s1 or not s2:
        return ''
    if s1[-1] == s2[-1]:
        return lcs(s1[:-1], s2[:-1]) + s1[-1]
    else:
        l1 = lcs(s1[:-1], s2)
        l2 = lcs(s1, s2[:-1])
        return l1 if len(l1) > len(l2) else l2
动态规划算法

动态规划算法是将递归算法中的重复计算缓存起来,避免重复计算,从而降低时间复杂度。LCS问题的动态规划解法通常使用二维数组来缓存计算结果。

先定义一个二维数组dp,dp[i][j]表示s1前i个字符和s2前j个字符的LCS的长度。当s1[i-1]==s2[j-1]时,dp[i][j] = dp[i-1][j-1]+1,否则dp[i][j] = max(dp[i-1][j], dp[i][j-1])。

def lcs(s1, s2):
    m, n = len(s1), len(s2)
    dp = [[0] * (n+1) for _ in range(m+1)]
    for i in range(1, m+1):
        for j in range(1, n+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])
    return dp[m][n]

动态规划算法的时间复杂度为O(mn),m和n分别为s1和s2的长度。

总结

最长的公共子序列是一道经典的算法问题,可以使用递归和动态规划两种算法来求解。递归算法简单易懂,但时间复杂度较高;动态规划算法复杂度较低,但需要理解动态规划的思想,并且需要定义一个二维数组来缓存计算结果。