20240819 代码随想录 | 动态规划 子序列

53. 最大子序和

贪心:

class Solution:
    def maxSubArray(self, nums):
        result = float('-inf')  # 初始化结果为负无穷大
        count = 0
        for i in range(len(nums)):
            count += nums[i]
            if count > result:  # 取区间累计的最大值(相当于不断确定最大子序终止位置)
                result = count
            if count <= 0:  # 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
                count = 0
        return result

动态规划:

dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]

class Solution:
    def maxSubArray(self, nums):
        dp = [0] * len(nums)
        dp[0] = nums[0]
        result = nums[0]
        for i in range(1, len(nums)):
            dp[i] = max(dp[i-1] + nums[i], nums[i])
            result = max(result, dp[i])
        return result

392.判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

我的解法:
 

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        count = len(s)
        j = 0
        for i in range(len(t)):
            if j == len(s):
                return True
            if t[i] == s[j]:
                j += 1
        return True if j == len(s) else False

动态规划:
同最长公共子序列,最后判断最长公共子序列长度是否等于len(s)

在确定递推公式的时候,首先要考虑如下两种操作,整理如下:

  • if (s[i - 1] == t[j - 1])
    • t中找到了一个字符在s中也出现了
  • if (s[i - 1] != t[j - 1])
    • 相当于t要删除元素,继续匹配

if (s[i - 1] == t[j - 1]),那么dp[i][j] = dp[i - 1][j - 1] + 1;,因为找到了一个相同的字符,相同子序列长度自然要在dp[i-1][j-1]的基础上加1

if (s[i - 1] != t[j - 1]),此时相当于t要删除元素,t如果把当前元素t[j - 1]删除,那么dp[i][j] 的数值就是 看s[i - 1]与 t[j - 2]的比较结果了,即:dp[i][j] = dp[i][j - 1];

区别就是 本题 如果删元素一定是字符串t,因为这里s是一定要匹配的,而 1143.最长公共子序列 是两个字符串都可以删元素。

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        dp = [[0]* (len(t)+1) for _ in range(len(s) + 1)]
        result = 0
        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] + 1
                else:
                    dp[i][j] = dp[i][j-1]
                result = max(result, dp[i][j])
        return result == len(s)

115.不同的子序列

给你两个字符串 s 和 t ,统计并返回在 s 的 子序列 中 t 出现的个数,结果需要对 109 + 7 取模。

class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        dp = [[0] * (len(t)+1) for _ in range(len(s)+1)]
        for i in range(len(s)):
            dp[i][0] = 1
        for j in range(1, len(t)):
            dp[0][j] = 0
        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. 两个字符串的删除操作

给定两个单词 word1 和 word2 ,返回使得 word1 和  word2 相同所需的最小步数

每步 可以删除任意一个字符串中的一个字符。

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        result = 0
        dp = [[0]*(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])
        return len(word1) + len(word2) - 2*dp[-1][-1]

72. 编辑距离

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        dp = [[0] * (len(word2)+1) for _ in range(len(word1)+1)]
        # dp[i][j]表示包含world1[i-1]和包含world2[j-1]的字符串的编辑距离
        for i in range(1, len(word1) + 1):
            dp[i][0] = i
        for j in range(1, len(word2) + 1):
            dp[0][j] = j
        # dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最近编辑距离为dp[i][0]。那么dp[i][0]就应该是i,对word1里的元素全部做删除操作,即:dp[i][0] = i;同理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.插入 可以转换成删除
                    # world1插入 = world2删除
                    # eg: 
                    # word1 = "a" ,word2 = "ad",word1插入元素'd'
                    # word1 = "a" ,word2 = "ad",word2删除元素'd'
                    # 删除world1[j-1]:dp[i][j] = dp[i][j-1] + 1
                    # 2.删除:
                    # 删除world1[i-1]: dp[i][j] = dp[i-1][j] + 1
                    # 3.替换:
                    # dp[i][j] = dp[i-1][j-1] + 1
                    dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
        return dp[-1][-1]

647. 回文子串

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

我的解法:超出时间限制

class Solution:
    def countSubstrings(self, s: str) -> int:
        dp = [0] * len(s)
        # 初始化第一个字符为回文子串
        dp[0] = 1
        for i in range(1, len(s)):
            # 从当前位置向前检查所有可能的回文子串
            for j in range(i, -1, -1):
                if self.is_huiwen(s[j:i+1]):
                    dp[i] += 1
        return sum(dp)  # 返回所有回文子串的总数

    def is_huiwen(self, s: str) -> bool:
        p = 0
        q = len(s) - 1
        while p < q:
            if s[p] != s[q]:
                return False
            p += 1
            q -= 1
        return True

动态规划解法:

难点:依旧是构建动态规划数组

class Solution:
    def countSubstrings(self, s: str) -> int:
        dp = [[False]*len(s) for _ in range(len(s))]
        result = 0
        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 or dp[i+1][j-1]:
                        result += 1
                        dp[i][j] = True
        return result

双指针递归解法:

class Solution:
    def countSubstrings(self, s: str) -> int:
        result = 0
        for i in range(len(s)):
            result += self.count(s, i, i)
            result += self.count(s, i, i+1)
        return result
        
    def count(self, s, i, j):
        result = 0
        while i >= 0 and j < len(s) and s[i]==s[j]:
            i -= 1
            j += 1
            result += 1
        return result

516.最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

示例 1:

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。

注意:这里是子序列,不是子串!

 

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        #   a b a a c a
        # a 1 1 3 3 3 4
        # b 0 1 1 2 2 3
        # a 0 0 1 2 2 3
        # a 0 0 0 1 1 3
        # c 0 0 0 0 1 1
        # a 0 0 0 0 0 1
        dp = [[0]*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+1, len(s)):
                if s[i] == s[j]:
                    dp[i][j] = dp[i+1][j-1] + 2
                else:
                    dp[i][j] = max(dp[i+1][j], dp[i][j-1])
        return dp[0][-1]

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值