leetCode 583.两个字符串的删除操作 动态规划 + 优化空间复杂度(二维dp、一维dp)

news2024/11/26 9:56:48

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

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

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

示例 1:

输入: word1 = "sea", word2 = "eat"
输出: 2
解释: 第一步将 "sea" 变为 "ea" ,第二步将 "eat "变为 "ea"

示例  2:

输入:word1 = "leetcode", word2 = "etco"
输出:4

 一、递归搜索 + 保存计算结果 = 记忆化搜索

  • 二维memo数组 存储计算过的子问题的结果 
class Solution {
public:
    // 递归搜索 + 保存计算结果 = 记忆化搜索
    // 二维memo数组 存储过的子问题的结果
    int minDistance(string s, string t) {
        int m = s.size(),n = t.size(),memo[m][n]; // 二维memo数组 存储计算过的子问题的结果;
        memset(memo,-1,sizeof(memo));// -1 表示没有访问过
        function<int(int,int)> dfs = [&](int i,int j) -> int {
            if(i<0) //base case 当i指针越界,此时
                return j+1;
            if(j<0) //base case
                return i+1;
            if (memo[i][j] != -1) // memo中有当前遇到的子问题的解,直接拿来返回
                return memo[i][j];
            if (s[i] == t[j]) {  
			    memo[i][j] = dfs(i-1, j-1);
		    } else {
			    // memo[i][j] = min(min(dfs(i-1, j)+1,dfs(i, j-1)+1),dfs(i-1, j-1)+2);
                // memo[i][j] = min(dfs(i-1, j)+1,dfs(i, j-1)+1);
                memo[i][j] = min(dfs(i-1, j),dfs(i, j-1))+1;
		    }
            return memo[i][j];
        };
        return dfs(m-1,n-1);
    }
}

二、动态规划 与 递归 的区别 

  • 递归公式 
if (s[i] == t[j]) {  
    memo[i][j] = dfs(i-1, j-1);
} else {
    // memo[i][j] = min(min(dfs(i-1, j)+1,dfs(i, j-1)+1),dfs(i-1, j-1)+2);
    // memo[i][j] = min(dfs(i-1, j)+1,dfs(i, j-1)+1);
    memo[i][j] = min(dfs(i-1, j),dfs(i, j-1))+1;
}

递归是自上而下调用,子问题自下而上被解决,最后解决了整个问题,而dp是从base case 出发,通过在dp数组记录中间结果,自下而上地顺序地解决子问题

  • dp解法

1.确定dp数组(dp table)以及下标的含义

dp[i][j]:以i-1为结尾的字符串word1,和以j-1为结尾的字符串word2,想要达到相等,所需要删除元素的最少次数

2.确定递推公式

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);
}

3.dp数组初始化

从递推式可看出,dp[i][0] dp[0][j] 是一定要初始化的

  • dp[i][0]:word2为字符串,以 i-1 为结尾的字符串 word1 需要删除 个元素才能变成空串,和word2相同
  • dp[0][j]:word1为字符串,以 j-1 为结尾的字符串 word2 需要删除 个元素才能变成空串,和word1相同
  • dp[0][0]=0,因为两个空字符串相同,删除操作为0
for(int i=1;i<=m;++i) dp[i][0] = i;
for(int j=1;j<=n;++j) dp[0][j] = j;

4.确定遍历顺序

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

5.举例推导dp数组

 (1)动态规划 二维dp

class Solution {
public:
    int minDistance(string word1, string word2) {
        int m = word1.size(),n = word2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));

        for(int i=1;i<=m;++i) dp[i][0] = i;
        for(int j=1;j<=n;++j) dp[0][j] = j;

        for(int i=1;i<=m;++i) {
            for(int j=1;j<=n;++j) {
                if(word1[i-1] == word2[j-1]) dp[i][j] = dp[i-1][j-1];
                // else dp[i][j] = min(min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+2);
                else dp[i][j] = min(dp[i-1][j]+1,dp[i][j-1]+1);
            }
        }
        return dp[m][n];
    }
};
  • 时间复杂度: O(m * n)
  • 空间复杂度: O(m * n)

(2)动态规划 二维dp 优化空间 

class Solution {
public:  
    // 动态规划 二维dp 优化空间
    int minDistance(string word1, string word2) {
        int m = word1.size(),n = word2.size();
        // vector<vector<int>> dp(m+1,vector<int>(n+1));
        vector<vector<int>> dp(2,vector<int>(n+1));
        for(int j=1;j<=n;++j) dp[0][j] = j;
        for(int i=1;i<=m;++i) {
            dp[i%2][0] = i;
            for(int j=1;j<=n;++j) {
                if(word1[i-1] == word2[j-1]) dp[i % 2][j] = dp[(i-1)%2][j-1];
                else dp[i%2][j] = min(dp[(i-1)%2][j]+1,dp[i%2][j-1]+1);
            }
        }
        return dp[m%2][n];
    }
};
  • 时间复杂度: O(m * n)
  • 空间复杂度: O(n)

(3)动态规划 一维dp(滚动数组) 优化空间

class Solution {
public:  
    // 动态规划 一维dp(滚动数组) 优化空间
    int minDistance(string word1, string word2) {
        int m = word1.size(),n = word2.size();
        vector<int> dp(n+1);

        for(int j=1;j<=n;++j) dp[j] = j;
        for(int i=1;i<=m;++i) {
            // pre 代表dp[i-1][0]
            int pre = dp[0];
            // 初始化当前层的 dp[i][0]
            dp[0] = i;
            for(int j=1;j<=n;++j) {
                int tmp = dp[j];
                if(word1[i-1] == word2[j-1]) dp[j] = pre;
                else dp[j] = min(dp[j]+1,dp[j-1]+1);
                pre = tmp;
            }
        }
        return dp[n];
    }
};
  • 时间复杂度: O(m * n)
  • 空间复杂度: O(n)

本题除了这种解法外,还有这种解题思路:先求出最长公共子序列,然后 word1.size() + word2.size() - 两倍的最长公共子序列

求最长公共子序列,可以看我往期的这篇文章:leetCode 1143.最长公共子序列

(1)二维dp 

class Solution {
public: 
   int minDistance(string word1, string word2) {
        int m = word1.size(),n = word2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1)); 
        for(int i=1;i<=m;++i) {
            for(int j=1;j<=n;++j) {
                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 m + n - 2 * dp[m][n];
    }
};
  • 时间复杂度: O(m * n)
  • 空间复杂度: O(m * n)

(2)二维dp:优化空间 

class Solution {
public:  
    // 方法二 二维dp 优化空间
    int minDistance(string word1, string word2) {
        int m = word1.size(),n = word2.size();
        vector<vector<int>> dp(2,vector<int>(n+1)); 
        for(int i=1;i<=m;++i) {
            for(int j=1;j<=n;++j) {
                if(word1[i-1] == word2[j-1]) dp[i%2][j] = dp[(i-1)%2][j-1] + 1;
                else dp[i%2][j] = max(dp[(i-1)%2][j],dp[i%2][j-1]);
            }
        }
        return m + n - 2 * dp[m%2][n];
    }
};
  • 时间复杂度: O(m * n)
  • 空间复杂度: O(n)

(3)一维dp:优化空间

class Solution {
public:
    // 方法二 一维dp 优化空间
    int minDistance(string word1, string word2) {
        int m = word1.size(),n = word2.size();
        vector<int> dp(n+1); 
        for(int i=1;i<=m;++i) {
            int pre = dp[0];
            for(int j=1;j<=n;++j) {
                int tmp = dp[j];
                if(word1[i-1] == word2[j-1]) dp[j] = pre + 1;
                else dp[j] = max(dp[j],dp[j-1]);
                pre = tmp;
            }
        }
        return m + n - 2 * dp[n];
    }
};
  • 时间复杂度: O(m * n)
  • 空间复杂度: O(n)

参考和推荐文章、视频:

代码随想录 (programmercarl.com)

动态规划之子序列,还是为了编辑距离做铺垫 | LeetCode:583.两个字符串的删除操作_哔哩哔哩_bilibili

来自代码随想录课堂的截图:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1089492.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3.2.5:VBA对单元格操作的引申

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的劳动效率&#xff0c;而且可以提高数据处理的准确度。我推出的VBA系列教程共九套和一部VBA汉英手册&#xff0c;现在已经全部完成&#xff0c;希望大家利用、学习。 如果…

【Leetcode刷题(数据结构)】:三路划分与三数随机取中的思想实现快速排序的再优化

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法&#xff0c;其基本思想为&#xff1a;任取待排序元素序列中 的某元素作为基准值&#xff0c;按照该排序码将待排序集合分割成两子序列&#xff0c;左子序列中所有元素均小于基准值&#xff0c;右 子序列中所有元素均…

JWT前后端分离在项目中的应用

14天阅读挑战赛当你累了&#xff0c;要学会休息&#xff0c;而不是放弃&#xff01; 目录 一、JWT简介 1.1 什么是JWT 1.2 为什么要使用JWT&#xff0c;与session的区别 1.3 JWT组成及工作原理和流程 二、JWT工具类解析 2.1 生成JWT 2.2 解析oldJwt 2.3 复制JWT并延时…

4.04 用户中心-我的订单评价

内容实现效果&#xff1a; 代码实现内容查看&#xff1a; http://www.gxcode.top/code

Unity中Shader光照模型Blinn-Phong原理及实现

文章目录 前言一、Blinn-Phong原理二、Blinn-Phong实现最终代码 前言 Unity中Shader光照模型Blinn-Phong原理及实现&#xff0c;也是经验型光照模型。和Phong模型一样&#xff0c;都是用于实现高光效果 一、Blinn-Phong原理 可以看出&#xff1a;Blinn-Phong模型和Phong模型不…

排序:如何用快排思想在O(n)内查找第K大元素?

文章来源于极客时间前google工程师−王争专栏。 冒泡排序、插入排序、选择排序三种排序算法&#xff0c;时间复杂度都是O(n^2)&#xff0c;比较高&#xff0c;适合小规模数据的排序。 归并排序和快速排序两种时间复杂度O(nlogn)的排序算法&#xff0c;适合大规模的数据排序&am…

未授权和代码执行漏洞特征和检测方法

文章目录 一、Redis未授权访问二、MongoDB未授权访问三、Elasticsearch未授权访问四、Rsync未授权访问五、Windows RDP远程代码执行漏洞&#xff08;CVE-2019-0708&#xff09;六、Tomcat Web控制台弱口令七、WebLogic控制台弱口令&反序列化系列漏洞八、WebLogic SSRF(无检…

微服务设计原则:构建弹性和可维护的应用

文章目录 1. 单一职责原则2. 独立性和自治性3. 弹性和容错性4. API 网关5. 日志和监控6. 版本管理7. 自动化部署和持续集成8. 安全性9. 数据一致性10. 文档和通信拓展思考结论 &#x1f389;欢迎来到架构设计专栏~微服务设计原则&#xff1a;构建弹性和可维护的应用 ☆* o(≧▽…

Linux环境下Qt应用程序安装器(installer)制作

本文介绍Linux环境下Qt应用程序安装器(installer)的制作。 安装器(installer)是将应用程序安装到操作系统平台的可执行文件&#xff0c;它采用向导式对话框指导用户安装应用程序&#xff0c;如我们在Windows操作系统安装Office软件时&#xff0c;有1个向导让你选择安装哪些组件…

力扣:611. 有效三角形的个数

今日为大家分享一道力扣611有效三角形的个数&#xff01;本文将会为大家为大家讲解题目&#xff0c;然后算法思路&#xff0c;最后再进行代码的实现&#xff01;希望看完本文能对读者有一定的收获&#xff01; 一、题目描述 通过题目的描述可以看出&#xff0c;意思是给定一个…

[产品体验] GPT4识图功能

[产品体验] GPT4识图功能 图片配文字超强的OCR能力知识问答多图解释 打开chatgpt的时候突然发现能用识图了&#xff0c;赶紧去体验一下&#xff0c;大大的震撼… 图片配文字 超强的OCR能力 我传上去的图片并不清晰… 还能准确识别&#xff0c;orz &#xff01; 知识问答 多…

代码随想录Day18 LeetCode235 二叉搜索树的公共祖先 T701二叉搜索树中的插入操作 T140 删除二叉搜索树中的公共节点

LeetCode T235 二叉搜索树的公共祖先 题目链接235. 二叉搜索树的最近公共祖先 - 力扣&#xff08;LeetCode&#xff09; 题目思路 此题不涉及遍历顺序. 关于二叉搜索树的定义,这里我就不过多赘述了,前面几篇都说清楚了,根节点比左子树元素都大,比右子树元素都小,这道题我们就可…

计算机体系结构和操作系统

这篇文章的主要内容是冯诺依曼计算机体系结构和操作系统的理解。 目录 一.冯诺依曼计算机体系结构 二.操作系统的理解 一.冯诺依曼计算机体系结构 如图是冯诺依曼计算机体系结构&#xff0c;计算机本质就是对数据进行处理的机器&#xff0c;图中&#xff0c;数据从输入设备交给…

VMWare配置桥接

一、设置网络模式 二、编辑网卡配置 ip配置的子网掩码和默认网关保持和宿主机一致&#xff0c;ip局域网内不冲突。 # cd /etc/sysconfig/network-scriptslsvim ifcfg-ens160 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOnone DEFROUTEyes IPV4_FAILURE_FATALno IP…

【数据结构】双链表的相关操作(声明结构体成员、初始化、判空、增、删、查)

双链表 双链表的特点声明双链表的结构体成员双链表的初始化带头结点的双链表初始化不带头结点的双链表初始化调用双链表的初始化 双链表的判空带头结点的双链表判空不带头结点的双链表判空 双链表的插入&#xff08;按值插入&#xff09;头插法建立双链表带头结点的头插法每次调…

每日一题 1488. 避免洪水泛滥(中单,贪心,二分)

思路&#xff1a; 当某一天为晴天&#xff0c;可以选择抽水时&#xff0c;我们是不知道要抽哪一个的&#xff0c;最优解应该是抽接下来最近的要发洪水的湖泊&#xff0c;所以我们先把晴天的坐标保存下来&#xff0c;需要用的时候再拿出来需要注意的是&#xff0c;只有晴天发生…

【MATLAB源码-第46期】基于matlab的OFDM系统多径数目对比,有无CP(循环前缀)对比,有无信道均衡对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 OFDM&#xff08;正交频分复用&#xff09;是一种频域上的多载波调制技术&#xff0c;经常用于高速数据通信中。以下是关于多径数目、有无CP&#xff08;循环前缀&#xff09;以及有无信道均衡在OFDM系统中对误码率的影响&am…

Python对接海康威视机器视觉工业相机

一、下载MVS客户端 海康机器人-机器视觉-下载中心 二、解压并安装MVS客户端 三、找到MVS示例代码&#xff08;代码在MVS的安装位置&#xff09; 工业相机只允许单条连接&#xff0c;也就是说MVS如果连接了相机&#xff0c;python代码就无法获取数据&#xff0c;此时必须退出M…

计算机毕业设计 大学生选修选课系统的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…