最长公共子序列

文章目录

动态规划

最⻓公共⼦序列(Longest Common Subsequence,简称 LCS)

  • 状态:两个指针i,j 从后往前遍历s1和s2
  • dp数组:dp(i,j)或者dp[ i ][ j ]:对于s1[1…i]和s2[1…j],它们的LCS长度
  • 选择:设s1和s2的最长公共子序列为lcs。那么对于s1和s2中的每个字符,要么在lcs中,要么不在
    ⽤两个指针 i 和 j 从后往前遍历 s1 和 s2 ,如果 s1[i]==s2[j] ,那么这个字符⼀定在 lcs 中;否则的话, s1[i] 和 s2[j] 这两个字符⾄少有⼀个不在 lcs 中,需要丢弃⼀个。
    (对于 s1[i] 和 s2[j] 不相等的情况,两个字符都不在lcs中时:
    dp[i][j] = max(dp[i-1][j],dp[i][j-1],dp[i-1][j-1]) ,原因在于我们对 dp 数组的定义,因为 dp[i-1][j-1] 永远是三者中最⼩的,max 根本不可能取到它,所以不考虑这种情况了)
  • base case:索引为 0 的⾏和列表⽰空串, dp[0][…] 和 dp[…][0] 都应该初始化为 0
dp(i,j)
= 0 if (i=-1||j=-1)
= dp(i-1,j-1) if(s[i]==s[j])
= max(
	dp(i-1,j),dp(i,j-1)
) if(s[i]!=s[j])

对于字符串 s1 和 s2 ,⼀般来说都要构造⼀个这样的 DP table
为了⽅便理解此表,我们暂时认为索引是从 1 开始的,待会的代码中只要稍作调整即可。
dp[i][j] 的含义是:对于 s1[1…i] 和 s2[1…j] ,它们的 LCS ⻓度是 dp[i][j]
其中,d[2][4] 的含义就是:对于 “ac” 和 “babc” ,它们的LCS ⻓度是 2。
在这里插入图片描述
在这里插入图片描述
找状态转移⽅程的⽅法是,思考每个状态有哪些「选择」,只要我们能⽤正确的逻辑做出正确的选择,算法就能够正确运⾏。

class Solution(object):
    def longestCommonSubsequence(self, text1, text2):
        """
        :type text1: str
        :type text2: str
        :rtype: int
        """
        m, n = len(text1),len(text2)
        dp = [[0]*(n+1) for _ in range(m+1)]
        for i in range(1, m+1):
            for j in range(1, n+1):
                if(text1[i-1] == text2[j-1]):
                    #找到lcs中的字符
                    dp[i][j] = 1 + dp[i-1][j-1]
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])
        return dp[-1][-1]

  • ⼦序列类型的问题,穷举出所有可能的结果都不容易,⽽动态规划算法做的就是穷举 +剪枝,它俩天⽣⼀对⼉。所以可以说只要涉及⼦序列问题,⼗有⼋九都需要动态规划来解决,往这⽅⾯考虑就对了
  • dp table结构:m+1行,n+1列
    dp = [[0]*(n+1) for _ in range(m+1)]
  • if(text1[i-1] == text2[j-1]):
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值