leetcode-动态规划【子序列问题】

本文深入探讨了动态规划在解决最长上升子序列、最长公共子序列、最长连续递增序列、最长重复子数组、最大子序和、编辑距离等经典问题中的应用。通过多个实例详细解析了不同类型的子序列问题以及编辑距离问题的解决方案,展示了动态规划在字符串处理和数组分析中的强大能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

子序列(不连续)

300. 最长上升子序列

1143. 最长公共子序列

1035. 不相交的线

子序列(连续)

674. 最长连续递增序列

718. 最长重复子数组

53. 最大子序和

编辑距离

392. 判断子序列

115. 不同的子序列

583. 两个字符串的删除操作

72. 编辑距离

回文

647. 回文子串

516. 最长回文子序列


子序列(不连续)

300. 最长递增子序列

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # 以nums[i]结尾最长上升子序列的长度
        dp = [0 for _ in range(len(nums))]
        dp[0] = 1
        for i in range(1, len(nums)):
            max_len = 0
            for j in range(i):
                if nums[j] < nums[i]:
                    cur_len = dp[j] + 1
                else:
                    cur_len = 1
                max_len = max(max_len, cur_len)
            dp[i] = max_len
        return max(dp)

1143. 最长公共子序列

class Solution(object):
    def longestCommonSubsequence(self, text1, text2):
        """
        :type text1: str
        :type text2: str
        :rtype: int
        """
        # dp[i][j]:text1[0:i]与text2[0:j]最长子序列长度
        dp = [[0 for _ in range(len(text2))] for _ in range(len(text1))]
        for i in range(len(text1)):
            for j in range(len(text2)):
                if i == 0 and j == 0 and text1[0] == text2[0]:
                    dp[0][0] = 1
                if i == 0:
                    if dp[0][j-1] or text1[0] == text2[j]:
                        dp[0][j] = 1
                    continue
                if j == 0:
                    if dp[i-1][0] or text1[i] == text2[0]:
                        dp[i][0] = 1
                    continue
                if text1[i] == text2[j]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
        return dp[-1][-1]

1035. 不相交的线

 本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!和上一题的思路是一样的。

子序列(连续)

674. 最长连续递增序列

class Solution(object):
    def findLengthOfLCIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # 以nums[i]为结尾的数组连续递增子序列长度为dp[i]
        dp = [0 for _ in range(len(nums))]
        dp[0] = 1
        for i in range(1, len(nums)):
            if nums[i] > nums[i-1]:
                dp[i] = dp[i-1] + 1
            else:
                dp[i] = 1
        return max(dp)

718. 最长重复子数组

class Solution(object):
    def findLength(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: int
        """
        # dp[i][j]:以nums1[i]和nums2[j]结尾的两个子序列最长重复子数组长度
        dp = [[0 for _ in range(len(nums2))] for _ in range(len(nums1))]
        ans = 0
        for i in range(len(nums1)):
            for j in range(len(nums2)):
                if nums1[i] != nums2[j]: dp[i][j] = 0
                else:
                    if i == 0 or j == 0: dp[i][j] = 1
                    else:
                        dp[i][j] = dp[i-1][j-1] + 1
                ans = max(ans, dp[i][j])
        return ans

53. 最大子序和

# 巧妙的解法
class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        result = -float('inf')
        ans = 0
        for i in range(len(nums)):
            ans += nums[i]
            if ans > result: result = ans
            if ans <= 0: ans = 0
        return result
class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # dp[i]:以nums[i]为结尾的最大连续子数组和
        dp = [nums[0]]
        for i in range(1, len(nums)):
            dp.append(max(dp[i-1]+nums[i], nums[i]))
        return max(dp)

编辑距离

392. 判断子序列

# dp[i][j]:s[:j]是否为t[:i]的子序列 0/1
class Solution(object):
    def isSubsequence(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        if len(s) == 0: return True
        if len(s) > len(t): return False
        # dp[i][j]:s[:j]是否为t[:i]的子序列 0/1
        dp = [[0 for _ in range(len(s))] for _ in range(len(t))]
        for i in range(len(t)):
            for j in range(len(s)):
                if i == 0 and j == 0:
                    if s[0] == t[0]: dp[i][j] = 1
                elif i == 0:
                    continue
                elif i < j:
                    continue
                else:
                    if t[i] == s[j]:
                        if j > 0: dp[i][j] = dp[i-1][j-1]
                        else: dp[i][j] = 1
                    else:
                        dp[i][j] = dp[i-1][j]
        return dp[-1][-1] == 1
# dp[i][j]:t[:i]和s[:j]的公共子序列长度
class Solution(object):
    def isSubsequence(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        if len(s) == 0: return True
        if len(t) < len(s): return False
        # dp[i][j]:t[:i]和s[:j]的公共子序列长度
        dp = [[0 for _ in range(len(s))] for _ in range(len(t))]
        for i in range(len(t)):
            for j in range(len(s)):
                if t[i] == s[j]:
                    if i == 0 or j == 0: dp[i][j] = 1
                    else: dp[i][j] = dp[i-1][j-1] + 1
                else:
                    if i == 0 and j == 0: dp[i][j] = 0
                    elif i == 0: dp[i][j] = dp[i][j-1]
                    elif j == 0: dp[i][j] = dp[i-1][j]
                    else: dp[i][j] = max(dp[i][j-1], dp[i-1][j])
        return dp[-1][-1] == len(s)

115. 不同的子序列

class Solution(object):
    def numDistinct(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: int
        """
        # dp[i][j]:s[:i]中存在t[:j]的个数
        dp = [[0 for _ in range(len(t)+1)] for _ in range(len(s)+1)]
        # 当t为空序列时设置s包含一个t
        for i in range(len(s)+1):
            dp[i][0] = 1
        for i in range(1, len(s)+1):
            for j in range(1, len(t)+1):
                if s[i-1] == t[j-1]:
                    dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
                else:
                    dp[i][j] = dp[i-1][j]
        return dp[-1][-1]

583. 两个字符串的删除操作

class Solution(object):
    def minDistance(self, word1, word2):
        """
        :type word1: str
        :type word2: str
        :rtype: int
        """
        # dp[i][j]:在word1[:i]和word2[:j]中最长公共子序列长度
        dp = [[0 for _ in range(len(word2)+1)] for _ in range(len(word1)+1)]
        for i in range(1, len(word1)+1):
            for j in range(1, len(word2)+1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])
        max_len = dp[-1][-1]
        return len(word1) + len(word2) - 2*max_len
class Solution(object):
    def minDistance(self, word1, word2):
        """
        :type word1: str
        :type word2: str
        :rtype: int
        """
        # dp[i][j]:word1[:i]和word2[:j]变成一样的序列所需要的操作数
        dp = [[0 for _ in range(len(word2)+1)] for _ in range(len(word1)+1)]
        for i in range(len(word1)+1):
            dp[i][0] = i
        for j in range(len(word2)+1):
            dp[0][j] = j
        for i in range(1, len(word1)+1):
            for j in range(1, len(word2)+1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+2)
        return dp[-1][-1]

72. 编辑距离

class Solution(object):
    def minDistance(self, word1, word2):
        """
        :type word1: str
        :type word2: str
        :rtype: int
        """
        # dp[i][j]:word1[:i]和word2[:j]变成一样的序列所需要的操作数
        dp = [[0 for _ in range(len(word2)+1)] for _ in range(len(word1)+1)]
        for i in range(len(word1)+1):
            dp[i][0] = i
        for j in range(len(word2)+1):
            dp[0][j] = j
        for i in range(1, len(word1)+1):
            for j in range(1, len(word2)+1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    # 1. 插入:word1删除相当于word2插入,word2删除相当于word1插入
                    # 2. 删除:
                    # 2.1 word1删除:dp[i][j] = dp[i-1][j]+1
                    # 2.2 word2删除:dp[i][j] = dp[i][j-1]+1
                    # 3. 替换:dp[i][j] = dp[i-1][j-1]+1
                    dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+1)
        return dp[-1][-1]

回文

647. 回文子串

class Solution(object):
    def countSubstrings(self, s):
        """
        :type s: str
        :rtype: int
        """
        count = 0
        # dp[i][j]:s[i:j]内的子串是否为回文子串
        dp = [[0 for _ in range(len(s))] for _ in range(len(s))]
        # 从左下角开始遍历
        for i in range(len(s)-1, -1, -1):
            for j in range(i, len(s)):
                if s[i] == s[j]:
                    if j-i <= 1:
                        dp[i][j] = 1
                        count += 1
                    else:
                        if dp[i+1][j-1]:
                            dp[i][j] = 1
                            count += 1
        return count

516. 最长回文子序列

class Solution(object):
    def longestPalindromeSubseq(self, s):
        """
        :type s: str
        :rtype: int
        """
        # dp[i][j]:s[i:j]内的最长回文子串长度
        dp = [[0 for _ in range(len(s))] for _ in range(len(s))]
        for i in range(len(s)):
            dp[i][i] = 1
        # 从左下角开始遍历
        for i in range(len(s)-1, -1, -1):
            for j in range(i, len(s)):
                if s[i] == s[j]:
                    if j-i <= 1: dp[i][j] = j-i+1
                    else: dp[i][j] = dp[i+1][j-1]+2
                else:
                    dp[i][j] = max(dp[i][j-1], dp[i+1][j])
        return dp[0][-1]
LeetCode 题目 491 - 递增子序列 (Incremental Subsequence) 是一道关于算法设计的中等难度题目。这道题要求你在给定整组 nums 中找出所有长度大于等于 1 的递增子序列。递增子序列是指组中的一串连续元素,它们按照顺序严格增大。 解决这个问题的一个常见策略是使用动态规划(Dynamic Programming),特别是哈希表或者单调栈(Monotonic Stack)。你可以维护一个栈,每当遍历到一个比栈顶元素大的字时,就将它推入栈,并更新当前最长递增子序列的长度。同时,如果遇到一个不大于栈顶元素的字,就从栈顶开始检查是否存在更长的递增子序列。 以下是 C++ 解决此问题的一种简单实现: ```cpp class Solution { public: vector<int> lengthOfLIS(vector<int>& nums) { int n = nums.size(); if (n == 0) return {}; // 使用单调栈存储当前已知的最大子序列 stack<pair<int, int>> stk; stk.push({nums[0], 1}); for (int i = 1; i < n; ++i) { while (!stk.empty() && nums[i] > stk.top().first) { // 如果新大于栈顶元素,找到一个更长的递增子序列 int len = stk.top().second + 1; ans.push_back(len); stk.pop(); } // 如果新不大于栈顶元素,尝试从当前位置开始寻找更长子序列 if (!stk.empty()) { stk.top().second = max(stk.top().second, 1); } else { stk.push({nums[i], 1}); } } return ans; } private: vector<int> ans; }; ``` 在这个解决方案中,`ans` 存储所有的递增子序列长度,最后返回这个结果向量即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值