Day45 - 动态规划 Part12

46 阅读3分钟

基础

加油!!!

刷题

  1. 不同的子序列

leetcode.cn/problems/di…

image.png

动规五部曲:

  • 确定dp数组含义

dp[i][j] 以 s[j-1]为结尾的字符串 中 以t[i-1]为结尾的字符串的 出现个数

  • 确定dp数组递推公式
if s[j-1] == t[i-1] {
    dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
    // 这里举个例子
    // s 为 rabb, t为rab
    // 最后一个元素b相等,dp[i-1][j-1]就是 s为 rab , t为ra 的出现个数,是1
    // 这里没有考虑 s为 rab ,t为 rab, 就是dp[i][j-1]
    // 当 s为rabbb, t为rab
    // 也是两种情况:
    // s为rabb, t为ra
    // s为rabb, t为rab,这种情况是第一个例子。第一个例子已经考虑了 s为 rab ,t为 rab
    // 所以就不需要深入到 dp[i][j-2]了
    // 这就是递归的意义
}else {
    dp[i][j] = dp[i][j-1]
    // 需要画图理解
}

  • 确定dp数组初始化
dp[0][j] = 1

dp[i][0] = 0.  i!= 0

//画图理解

IMG_3400.HEIC

  • 确定dp数组遍历顺序

从上到下,从左到右

  • 打印dp数组
  1. 两个字符串的删除操作

leetcode.cn/problems/de…

image.png

这个是求两个字符串最长公共子序列的思路,反推出最小需要删除的操作次数

image.png

第二个直接使用最小删除次数作为dp数组的value

动规五部曲(第二种方法):

  • 确定dp数组含义

dp[i][j] 以word1[i-1]为结尾的字符串 和 以word2[j-1]为结尾的字符串变成相同,需要删除的次数

  • 确定dp数组递推公式
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)
    // 这里当word1[i-1] != word2[j-1]的时候
    // dp[i-1][j] 是删除word1 最后一个字母的最小删除次数, 因为我们已经考虑删除了一个字母,故+1
    //第二个一样
    //dp[i-1][j-1]+2  可以不写,因为前两个已经包含
}

  • 确定dp数组初始化

当word1 = ‘’, word2需要全部删除才能保持一样

dp[0][j] = j

dp[i][0] = i

  • 确定dp数组遍历顺序

从左到右,从上到下

  • 打印dp数组
  1. 编辑距离 (经典题目)

leetcode.cn/problems/ed…

image.png

动规五部曲:

  • 确定dp数组含义

dp[i][j] 以word1[i-1]为结尾的字符串 转换成 word2[j-1]为结尾的字符串需要的最小操作次数

  • 确定dp数组递推公式
if word1[i-1] == word2[j-1] {
    dp[i][j] = dp[i-1][j-1]
}else {
    //三种操作方式
    //删除 删除word1,即 dp[i-1][j]+1
    //替换 替换word1最后一个字母和word2保持一致,即 dp[i-1][j-1]+1
    //增加 增加word1的元素,和word2保持一致,即 dp[i][j-1] + 1
    dp[i][j] = min(dp[i-1][j-1]+1, dp[i-1][j]+1, dp[i][j-1]+1)
}

  • 确定dp数组初始化
dp[i][0] = i

dp[0][j] = j

  • 确定dp数组遍历顺序

从左到右,从上到下

  • 打印dp数组

总结

  • 动态规划核心思想是记录递归的状态,而不需要每一次都从头遍历,需要dp[2]中记录dp[1],这样dp[3]就不会从dp[1]开始计算,而是直接拿dp[2]