算法第45天:动态规划part12(第九章)

发布于:2025-08-15 ⋅ 阅读:(13) ⋅ 点赞:(0)

1.不同的子序列

115. 不同的子序列 - 力扣(LeetCode)

 状态转移公式
对于 s[i-1]t[j-1]

  • 如果 相等

    • 可以选择用这两个字符匹配(dp[i-1][j-1]

    • 也可以选择不用 s[i-1](dp[i-1][j]

    • 因此:

      dp[i][j]=dp[i−1][j−1]+dp[i−1][j]dp[i][j] = dp[i-1][j-1] + dp[i-1][j]dp[i][j]=dp[i−1][j−1]+dp[i−1][j]
  • 如果 不相等

    • 只能跳过 s[i-1]:

      dp[i][j]=dp[i−1][j]dp[i][j] = dp[i-1][j]dp[i][j]=dp[i−1][j]
class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        #dp[i][j] 表示以下标i-1结尾的s子序列出现以j-1为结尾的t的个数是dp[i][j]
        m = len(s)
        n = len(t)
        if m < n:
            return 0
        dp = [[0]*(n+1) for _ in range(m+1)]
        for i in range(m):
            dp[i][0] = 1
        for j in range(1, n+1):
            dp[0][j] = 0
        for i in range(1, m+1):
            for j in range(1, n+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]
        
        

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

583. 两个字符串的删除操作 - 力扣(LeetCode)

思路:

1. 状态定义

dp[i][j] 表示:

  • word1i 个字符 和 word2j 个字符 变成相同字符串所需的最少删除次数。


2. 初始化

  • dp[i][0] = i:如果 word2 是空串,只能删除 word1 的所有 i 个字符。

  • dp[0][j] = j:如果 word1 是空串,只能删除 word2 的所有 j 个字符。


3. 状态转移

  1. 如果 word1[i-1] == word2[j-1]

    • 末尾字符相同,不需要额外操作:

      dp[i][j]=dp[i−1][j−1]dp[i][j] = dp[i-1][j-1]dp[i][j]=dp[i−1][j−1]
  2. 如果 word1[i-1] != word2[j-1]

    • 删除 word1[i-1]dp[i-1][j] + 1

    • 删除 word2[j-1]dp[i][j-1] + 1

    • 同时删除两个末尾字符:dp[i-1][j-1] + 2

    • 取最小值:

      dp[i][j]=min⁡(dp[i−1][j]+1, dp[i][j−1]+1, dp[i−1][j−1]+2)dp[i][j] = \min(dp[i-1][j] + 1,\ dp[i][j-1] + 1,\ dp[i-1][j-1] + 2)dp[i][j]=min(dp[i−1][j]+1, dp[i][j−1]+1, dp[i−1][j−1]+2)

4. 返回结果

  • dp[m][n] 即为将 word1word2 变成相同所需的最少删除步数。

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        #dp[i][j] 表示以i-1结尾的word1子序列和以j-1结尾的word2子序列中使其相同的的最小步数是dp[i][j]
        m = len(word1)
        n = len(word2)
    
        dp = [[0]*(n+1) for _ in range(m+1)]
        for i in range(m+1):
            dp[i][0] = i
        for j in range(1, n+1):
            dp[0][j] = j

        for i in range(1, m+1):
            for j in range(1, n+1):
                if word1[i-1] != word2[j-1]:
                    dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+2)
                else:
                    dp[i][j] = dp[i-1][j-1]
        return dp[-1][-1]

3.编辑距离

72. 编辑距离 - 力扣(LeetCode)

思路:

1. 定义状态
dp[i][j] 表示将 word1 的前 i 个字符(word1[:i])转换成 word2 的前 j 个字符(word2[:j])所需的最少操作数。


2. 初始化

  • dp[i][0] = iword2 为空,只能删除 i

  • dp[0][j] = jword1 为空,只能插入 j


3. 状态转移公式
比较 word1[i-1]word2[j-1]

  • 如果相等:

    dp[i][j]=dp[i−1][j−1]dp[i][j] = dp[i-1][j-1]dp[i][j]=dp[i−1][j−1]
  • 如果不相等:三种操作取最小值:

    1. 删除(来自上方):dp[i-1][j] + 1

    2. 插入(来自左方):dp[i][j-1] + 1

    3. 替换(来自左上):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)dp[i][j] = \min(dp[i-1][j] + 1, \ dp[i][j-1] + 1, \ 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)

4. 最终答案
dp[m][n] 即将 word1 转换为 word2 所需的最少操作数,其中 m = len(word1), n = len(word2)

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        #dp[i][j] 表示以下标i-1结尾的word1转换为以下标j-1结尾的word2所使用的最少操作数是dp[i][j]

        #初始化
        m, n = len(word1), len(word2)
        
        dp = [[0]*(n+1) for _ in range(m+1)]
        for i in range(m+1):
            dp[i][0] = i
        for j in range(1, n+1):
            dp[0][j] = j
        for i in range(1, m+1):
            for j in range(1, n+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-1][j-1]+1, dp[i][j-1]+1) #删除,替换,插入
        return dp[-1][-1]

动态规划之编辑距离总结篇

判断子序列

  • 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;
else dp[i][j] = dp[i][j - 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];
}

两个字符串的删除操作

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] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 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 - 1][j], dp[i][j - 1]}) + 1;
}

网站公告

今日签到

点亮在社区的每一天
去签到