算法训练 | 动态规划Part12 | 115.不同的子序列、72. 编辑距离

devtools/2024/11/13 9:10:10/

目录

115.不同的子序列

动态规划

72.编辑距离 ⭐

动态规划


115.不同的子序列

  • 题目链接:115. 不同的子序列 - 力扣(LeetCode)

  • 文章讲解:代码随想录

动态规划
  • 解题思路

    • 这道题目如果不是子序列,而是要求连续序列的,那就可以考虑用KMP。这道题目相对于72. 编辑距离,简单了不少,因为本题相当于只有删除操作,不用考虑替换增加之类的。但相对于动态规划:392.判断子序列 (opens new window)就有难度了

  • 解题步骤

      当s[i - 1] 与 t[j - 1]相等时,dp[i][j]可以有两部分组成。一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。即不需要考虑当前s子串和t子串的最后一位字母,所以只需要 dp[i-1][j-1]。一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]。所以当s[i - 1] 与 t[j - 1]相等时,dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];

      当s[i - 1] 与 t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配(就是模拟在s中删除这个元素),即:dp[i - 1][j]。所以递推公式为:dp[i][j] = dp[i - 1][j];

    • 确定dp数组(dp table)以及下标的含义:dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。

    • 确定递推公式

      • s[i - 1] 与 t[j - 1]相等

      • s[i - 1] 与 t[j - 1] 不相等

    • dp数组如何初始化:从递推公式dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; 和 dp[i][j] = dp[i - 1][j]; 中可以看出dp[i][j] 是从上方和左上方推导而来,如图:,那么 dp[i][0] 和dp[0][j]是一定要初始化的。那么dp[i][0]一定都是1,因为也就是把以i-1为结尾的s,删除所有元素,出现空字符串的个数就是1。dp[0][j],dp[0][j]:空字符串s可以随便删除元素,出现以j-1为结尾的字符串t的个数。dp[0][j]一定都是0,s如论如何也变成不了t。最后就要看一个特殊位置了,dp[0][0]应该是1,空字符串s,可以删除0个元素,变成空字符串t。

    • 确定遍历顺序:从递推公式dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; 和 dp[i][j] = dp[i - 1][j]; 中可以看出dp[i][j]都是根据左上方和正上方推出来的。所以遍历的时候一定是从上到下,从左到右,这样保证dp[i][j]可以根据之前计算出来的数值进行计算。

    • 举例推导dp数组:

  • 代码一:动态规划

class Solution {
public:int numDistinct(string s, string t) {vector<vector<uint64_t>> dp(s.size() + 1, vector<uint64_t>(t.size() + 1));for (int i = 0; i < s.size(); i++) dp[i][0] = 1;for (int j = 1; j < t.size(); j++) dp[0][j] = 0;for (int i = 1; i <= s.size(); i++) {for (int j = 1; j <= t.size(); j++) {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[s.size()][t.size()];}
};

72.编辑距离 ⭐

  • 题目链接:72. 编辑距离 - 力扣(LeetCode)

  • 文章讲解:代码随想录

动态规划
  • 解题思路

    • 编辑距离是用动规来解决的经典题目,这道题目看上去好像很复杂,但用动规可以很巧妙的算出最少编辑距离。

  • 解题步骤

      在整个动规的过程中,最为关键就是正确理解dp[i][j]的定义!if (word1[i - 1] != word2[j - 1]),此时就需要编辑了
    • 操作一:word1删除一个元素,那么就是以下标i - 2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 再加上一个操作。即 dp[i][j] = dp[i - 1][j] + 1;

    • 操作二:word2删除一个元素,那么就是以下标i - 1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 再加上一个操作。即 dp[i][j] = dp[i][j - 1] + 1;

    • 操作三:替换元素,word1替换word1[i - 1],使其与word2[j - 1]相同,此时不用增删加元素。

      综上,当 if (word1[i - 1] != word2[j - 1]) 时取最小的,即:dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;

    • 确定dp数组(dp table)以及下标的含义:dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。

      • if (word1[i - 1] == word2[j - 1]) 那么说明不用任何编辑,dp[i][j] 就应该是 dp[i - 1][j - 1],即dp[i][j] = dp[i - 1][j - 1];word1[i - 1]word2[j - 1]相等了,那么就不用编辑了,以下标i-2为结尾的字符串word1和以下标j-2为结尾的字符串word2的最近编辑距离dp[i - 1][j - 1]就是 dp[i][j]了。

    • dp数组如何初始化:再回顾一下dp[i][j]的定义:dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最近编辑距离为dp[i][0]。那么dp[i][0]就应该是i,对word1里的元素全部做删除操作,即:dp[i][0] = i;同理dp[0][j] = j;

    • 确定遍历顺序

          从如下四个递推公式:
      • dp[i][j] = dp[i - 1][j - 1]

      • dp[i][j] = dp[i - 1][j - 1] + 1

      • dp[i][j] = dp[i][j - 1] + 1

      • dp[i][j] = dp[i - 1][j] + 1

    • 举例推导dp数组:

  • 代码一:动态规划

class Solution {
public:int minDistance(string word1, string word2) {vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;for (int i = 1; i <= word1.size(); i++) {for (int j = 1; j <= word2.size(); 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], dp[i - 1][j], dp[i][j - 1]}) + 1;}}}return dp[word1.size()][word2.size()];}
};


http://www.ppmy.cn/devtools/56201.html

相关文章

如何使用Vue.js实现动态文档生成与下载功能

在现代Web应用开发中&#xff0c;用户往往需要在浏览器端完成复杂的操作&#xff0c;如生成和下载特定格式的文档&#xff0c;而无需服务器直接干预。本文将以一个Vue.js应用程序为例&#xff0c;详细介绍如何利用axios&#xff08;或自定义请求模块&#xff09;结合FileReader…

【小工具】导出Unity资源的预览缩略图

ExportPreviewTools 介绍 导出Unity资源的预览缩略图 使用场景 在某些情况下想要展示拥有的模型资源进行预览时&#xff0c;可以使用其快速预览功能。 工具原理 Selection类 //获取选中的资源的GUID string[] assetGUIDs Selection.assetGUIDs;AssetDatabase类操作资源…

【图像处理】1、使用OpenCV库图像轮廓的检测和绘制

OpenCV (Open Source Computer Vision Library) 是一个用于计算机视觉和图像处理的开源库。它提供了数百种用于图像和视频分析的算法&#xff0c;并被广泛应用于研究和商业领域。OpenCV 支持多种编程语言&#xff0c;包括 C、Python、Java 等&#xff0c;具有跨平台的特性&…

Cyuyanzhong的内存函数

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、memcpy函数的使用与模拟实现二、memmove函数的使用和模拟实现三、memset函数与memcmp函数的使用&#xff08;一&#xff09;、memset函数&#xff08;内存块…

制造企业的仓库管理如何做好数据分析?

在竞争激烈的现代制造业环境中&#xff0c;仓库管理成为许多生产制造企业面临的一大挑战。随着产品种类的不断增加和客户需求的日一个型号&#xff0c;仓库不仅要处理物料、半成品和成品&#xff0c;还要应对产品更新换代、不同项目客户的特殊需求等复杂因素。面对这些挑战&…

Vue.js中的计算属性:如何让数据自动更新

引言 在Vue.js的世界里&#xff0c;computed属性就像是你的智能助手&#xff0c;它能自动追踪变化&#xff0c;帮你快速做出反应。想象一下&#xff0c;你在做一道菜&#xff0c;调料&#xff08;数据&#xff09;一变&#xff0c;味道&#xff08;界面&#xff09;立刻跟上。…

【干货】微信小程序免费开源项目合集

前言 2024年了&#xff0c;还有小伙伴在问微信小程序要怎么开发&#xff0c;有什么好的推荐学习项目可以参考的。今天分享一个收集了一系列在微信小程序开发中有用的工具、库、插件和资源&#xff1a;awesome-github-wechat-weapp。 开源项目介绍 它提供了丰富的资源列表&…

threejs的学习(二)

上次我们说了如何搭建环境&#xff0c;并成功显示了一个静止的方块&#xff0c; 1.通过轨道控制器查看方块&#xff0c;同时添加坐标轴辅助器 main.js import * as THREE from "../assets/js/three.module"; import { OrbitControls } from "../../../three.j…