动态规划-序列型动态规划

文章介绍了如何使用动态规划方法解决LeetCode中关于回文子串和子序列的问题。通过Python代码展示了不同题目的解题思路,包括最长回文子串、最长回文子序列、最长公共子序列和最长重复子数组,以及编辑距离问题。每个问题的核心在于确定动态转移方程并初始化状态。
摘要由CSDN通过智能技术生成

1 回文子串

# Given a string s, return the longest palindromic substring in s. 
# 
#  
#  Example 1: 
# 
#  
# Input: s = "babad"
# Output: "bab"
# Explanation: "aba" is also a valid answer.
#  
# 
#  Example 2: 
# 
#  
# Input: s = "cbbd"
# Output: "bb"
#  
# 
#  
#  Constraints: 
# 
#  
#  1 <= s.length <= 1000 
#  s consist of only digits and English letters. 
#  
import pandas as pd

# leetcode submit region begin(Prohibit modification and deletion)
class Solution:
    def longestPalindrome(self, s: str) -> str:
        # i和j分别存储起始点和终点,数值存长度的大小
        # f[i][j] = f[i+1][j-1] + 2 if s[i-1] == s[j-1] else f[i][j] = 0
        n = len(s)
        f = [[0] * (n) for _ in range(n)]
        max_len = 0
        x = -1
        y = -1
        for i in range(n - 1, -1, -1):
            for j in range(i, n, 1):
                if s[i] == s[j]:
                	# 初始化,回文子串的长度是奇数
                    if i == j:
                        f[i][j] = 1
                    else:
                    	# 初始化,回文子串的长度是偶数
                        if i + 1 > j - 1:
                            f[i][j] = 2
                        # 过程更新,确保转移之前子串是回文串
                        elif f[i + 1][j - 1]:
                            f[i][j] = f[i + 1][j - 1] + 2
                else:
                    f[i][j] = 0
                if f[i][j] > max_len:
                    x = i
                    y = j
                    max_len = f[i][j]
        return s[x:y + 1]


# leetcode submit region end(Prohibit modification and deletion)

if __name__ == '__main__':
    so = Solution()
    s = "babad"
    res = so.longestPalindrome(s)
    print(res)

1.1 解题思路

  • 确定f[i][j]的含义,表示以i子串开始,j子串结束,即f[i][j]回文子串的长度。
  • 确定动态转移方程,动态转移方程的核心f[i][j] = f[i + 1][j - 1] + 2 if s[i] == s[j]
  • 确定初始化条件,初始化条件包括奇数和偶数两种情况。
  • 确定遍历的顺序,i依赖i+1, j依赖j-1,所以对i进行倒序遍历,对j进行正序遍历

2 回文子序列

# Given a string s, find the longest palindromic subsequence's length in s. 
# 
#  A subsequence is a sequence that can be derived from another sequence by 
# deleting some or no elements without changing the order of the remaining elements. 
# 
#  
#  Example 1: 
# 
#  
# Input: s = "bbbab"
# Output: 4
# Explanation: One possible longest palindromic subsequence is "bbbb".
#  
# 
#  Example 2: 
# 
#  
# Input: s = "cbbd"
# Output: 2
# Explanation: One possible longest palindromic subsequence is "bb".
#  
# 
#  
#  Constraints: 
# 
#  
#  1 <= s.length <= 1000 
#  s consists only of lowercase English letters. 
#  


# leetcode submit region begin(Prohibit modification and deletion)
class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        # f[i][j] = f[i+1][j-1] + 2 if s[i]==s[j]
        # f[i][j] = max(f[i][j-1], f[i-1][j]) if s[i] != s[j]
        l = len(s)
        max_len = 0
        f = [[0] * l for _ in range(l)]
        for i in range(l - 1, -1, -1):
            for j in range(i, l, 1):
                if s[i] == s[j]:
                    if i == j:
                        f[i][j] = 1
                    else:
                        if i + 1 > j - 1:
                            f[i][j] = 2
                        else:
                            f[i][j] = f[i + 1][j - 1] + 2
                else:
                    f[i][j] = max(f[i][j - 1], f[i + 1][j])
                if f[i][j] > max_len:
                    max_len = f[i][j]
        return max_len


# leetcode submit region end(Prohibit modification and deletion)


if __name__ == '__main__':
    so = Solution()
    s = "bbbab"
    res = so.longestPalindromeSubseq(s)
    print(res)

2.1 解题思路

  • 回文子串和回文子序列关键差异在于动态转移方程不相等情况的处理,回文子串如果不相等,f[i][j] = 0,回文子序列则是f[i][j] = max(f[i][j - 1], f[i + 1][j])

3. 最长公共子序列

3.1 题意

1143. 最长公共子序列

3.2 代码

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> 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]:
                    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]

公共子序列需要从同时考虑左上[i - 1][j - 1]左边[i - 1][j]上边[i][j - 1]三个方向,如下代码:

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

4. 最长公共子串

4.1 题意

718. 最长重复子数组

4.2 代码

class Solution:
    def findLength(self, A: List[int], B: List[int]) -> int:
        n, m = len(A), len(B)
        dp = [[0] * (m + 1) for _ in range(n + 1)]
        ans = 0
        for i in range(n - 1, -1, -1):
            for j in range(m - 1, -1, -1):
                dp[i][j] = dp[i + 1][j + 1] + 1 if A[i] == B[j] else 0
                ans = max(ans, dp[i][j])
        return ans

公共子串只需要从同时考虑左上[i - 1][j - 1]一个方向,如下代码:

dp[i][j] = dp[i + 1][j + 1] + 1 if A[i] == B[j] else 0

5. 编辑距离

5.1 题意

72. 编辑距离

5.2 代码

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        n = len(word1)
        m = len(word2)
        
        # 有一个字符串为空串
        if n * m == 0:
            return n + m
        
        # DP 数组
        D = [ [0] * (m + 1) for _ in range(n + 1)]
        
        # 边界状态初始化
        for i in range(n + 1):
            D[i][0] = i
        for j in range(m + 1):
            D[0][j] = j
        
        # 计算所有 DP 值
        for i in range(1, n + 1):
            for j in range(1, m + 1):
                left = D[i - 1][j] + 1
                up = D[i][j - 1] + 1
                left_up = D[i - 1][j - 1] 
                if word1[i - 1] != word2[j - 1]:
                    left_up += 1
                D[i][j] = min(left, up, left_up)
        
        return D[n][m]

类似最长公共子序列,需要同时考虑左边left ,上面up ,左上left_up三个方向,代码如下:

 left = D[i - 1][j] + 1
 up = D[i][j - 1] + 1
 left_up = D[i - 1][j - 1] 
 if word1[i - 1] != word2[j - 1]:
     left_up += 1
 D[i][j] = min(left, up, left_up)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值