代码随想录算法训练营第四十八天| 115.不同的子序列、583. 两个字符串的删除操作、 72. 编辑距离

news2024/9/21 14:06:04

115.不同的子序列

在这里插入图片描述

题目链接:115.不同的子序列
文档讲解:代码随想录
状态:不会

思路:
dp[i][j] 表示在 s 的前 j 个字符中,t 的前 i 个字符作为子序列出现的次数。

匹配的情况:
1.当 s[j-1] 与 t[i-1] 匹配时,说明我们可以用 s[j-1] 来匹配 t[i-1],这种情况下,在 s 的前 j-1 个字符中找到的所有 t 的前 i-1 个字符作为子序列的组合数(dp[i-1][j-1])都可以作为当前组合的一部分。例如: t:ba(g) s:bag(g) ,当t[2]=s[3]时,如果使用s[3]的话,dp[2][3]可以由dp[1][2]得到,也就是t:ba(g)在s:bag(g)中的出现次数可以由ba在bag出现的次数推导过来,因为t和s都加上一个相同的g。这是推导dp[i][j]的可能情况之一。
2.此外,即使不使用 s[j-1] 也可以完成匹配,这种情况下,只需要在 s 的前 j-1 个字符中找到 t 的前 i 个字符作为子序列的组合数(dp[i][j-1])。例如:t:ba(g) s:bag(g) ,如果不使用s[3],就是看 t:ba(g) 和 s:ba(g) ,这种情况 dp[2][3] 可以由dp[2][2]得到,也就是t:ba(g)在s:bag(g)中的出现次数可以由t:bag在s:bag出现的次数推导过来。这也是推导dp[i][j]的一种情况。
所以: s[j-1] 与 t[i-1] 匹配时,dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];

不匹配的情况:
当 s[j-1] 与 t[i-1] 不匹配时,说明我们不能用 s[j-1] 来匹配 t[i-1]。这种情况下,我们只能在 s 的前 j-1 个字符中找到 t 的前 i 个字符作为子序列的组合数(dp[i][j-1])。

初始状态
空字符串 t 是任何字符串 s 的子序列:因此,对于任何 j,dp[0][j] = 1。
空字符串 s 不能包含非空字符串 t:因此,对于任何 i > 0,dp[i][0] = 0。

题解:

    public int numDistinct(String s, String t) {
        // 将字符串转换为字符数组
        char[] sChars = s.toCharArray();
        char[] tChars = t.toCharArray();

        // 创建DP数组,dp[i][j]表示t的前i个字符在s的前j个字符中出现的次数
        int[][] dp = new int[t.length() + 1][s.length() + 1];

        // 初始化 dp[0][j] 为 1,因为空字符串t是任何字符串s的子序列
        for (int j = 0; j <= s.length(); j++) {
            dp[0][j] = 1;
        }

        // 填充DP数组
        for (int i = 1; i <= t.length(); i++) {
            for (int j = 1; j <= s.length(); j++) {
                if (tChars[i - 1] == sChars[j - 1]) {
                    // 如果当前字符匹配,可以选择匹配和不匹配两种情况
                    dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];
                } else {
                    // 如果当前字符不匹配,只能不匹配
                    dp[i][j] = dp[i][j - 1];
                }
            }
        }

        // 返回t的前t.length()个字符在s的前s.length()个字符中出现的次数
        return dp[t.length()][s.length()];
    }

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

在这里插入图片描述

题目链接:583. 两个字符串的删除操作
文档讲解:代码随想录
状态:做出来了有点磕绊

思路1:找到最大公共子序列数量,然后两个字符串分别减去它求和。

思路2:
dp[i][j]表示word1[0,i],word2[0,j],要使它们相同一共需要删去多少个字符。

当word1[i - 1] 与 word2[j - 1]相同的时候,显然dp[i][j] = dp[i - 1][j - 1];

当word1[i - 1] 与 word2[j - 1]不相同的时候:
情况一:删word1[i - 1],最少操作次数为dp[i - 1][j] + 1
情况二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1
情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2
其中情况三隐含在情况一和二中,所以应该是情况1和2中应该取最小操作数,所以dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);

初始化:
dp[i][0]:word2为空字符串,以i-1为结尾的字符串word1要删除多少个元素,才能和word2相同呢,很明显dp[i][0] = i。
dp[0][j]的话同理

题解:

    public int minDistance(String word1, String word2) {
        // 将字符串转换为字符数组
        char[] w1 = word1.toCharArray();
        char[] w2 = word2.toCharArray();

        // 创建DP数组,dp[i][j]表示word1的前i个字符和word2的前j个字符的最长公共子序列长度
        int[][] dp = new int[word1.length() + 1][word2.length() + 1];

        // 填充DP数组
        for (int i = 1; i <= w1.length; i++) {
            for (int j = 1; j <= w2.length; j++) {
                if (w1[i - 1] == w2[j - 1]) {
                    // 如果当前字符匹配,LCS长度加1
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    // 如果当前字符不匹配,选择两个子问题中LCS最长的一个
                    dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
                }
            }
        }

        // 计算最少步骤:将两个字符串转换成它们的LCS需要的删除和插入操作数
        return word1.length() + word2.length() - 2 * dp[word1.length()][word2.length()];
    }

    public int minDistance2(String word1, String word2) {
        char[] w1 = word1.toCharArray();
        char[] w2 = word2.toCharArray();
        int m = word1.length(), n = word2.length();

        // 创建DP数组,dp[i][j]表示word1的前i个字符与word2的前j个字符变得相同所需删除的字符数
        int[][] dp = new int[m + 1][n + 1];

        // 初始化边界情况:将word1的前i个字符变为空字符串需要i步操作(全删除)
        for (int i = 1; i <= m; i++) {
            dp[i][0] = i;
        }

        // 初始化边界情况:将空字符串变成word2的前j个字符需要j步操作(全删除)
        for (int j = 1; j <= n; j++) {
            dp[0][j] = j;
        }

        // 填充DP数组
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (w1[i - 1] == w2[j - 1]) {
                    // 当前字符相等,不需要额外删除
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    // 当前字符不相等,选择删除word1[i - 1]或word2[j - 1]中的最小删除次数
                    dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
                }
            }
        }

        // 返回最终结果
        return dp[m][n];
    }

    //最少编辑距离思路
    public int minDistance3(String word1, String word2) {
        // 将字符串转换为字符数组
        char[] w1 = word1.toCharArray();
        char[] w2 = word2.toCharArray();
        int m = word1.length(), n = word2.length();

        // 创建DP数组,dp[i][j]表示将word1的前i个字符转换为word2的前j个字符所需的最少操作数
        int[][] dp = new int[m + 1][n + 1];

        // 初始化边界情况:word1的前i个字符转换为空字符串需要i步操作(全删除)
        for (int i = 1; i <= m; i++) {
            dp[i][0] = i;
        }

        // 初始化边界情况:空字符串转换为word2的前j个字符需要j步操作(全插入)
        for (int i = 1; i <= n; i++) {
            dp[0][i] = i;
        }

        // 填充DP数组
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (w1[i - 1] == w2[j - 1]) {
                    // 如果当前字符匹配,不需要额外操作
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    // 如果当前字符不匹配,选择三种操作中的最小值(插入、删除、替换)
                    dp[i][j] = Math.min(dp[i - 1][j] + 1, // 删除操作
                            Math.min(dp[i][j - 1] + 1, // 插入操作
                                    dp[i - 1][j - 1] + 1)); // 替换操作
                }
            }
        }

        // 返回将word1转换为word2所需的最少操作数
        return dp[m][n];
    }

72. 编辑距离

在这里插入图片描述

题目链接:72. 编辑距离
文档讲解:代码随想录
状态:不会

思路:
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];

if (word1[i - 1] != word2[j - 1]),此时就需要编辑了,如何编辑呢?

操作一:word1删除一个元素,那么就是word1[0,i-1] 与 word2[0,j] 的最近编辑距离 再加上一个操作。
即 dp[i][j] = dp[i - 1][j] + 1;

操作二:word2插入一个元素,那么就是word1[0,i] 与 word2[0,j-1] 的最近编辑距离 再加上一个操作。

操作三:替换元素,word1替换word1[i - 1],使其与word2[j - 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;
}

初始化:
word1的前i个字符转换为空字符串需要i步操作(全删除)
空字符串转换为word2的前j个字符需要j步操作(全插入)

题解:

    public int minDistance(String word1, String word2) {
        // 将字符串转换为字符数组
        char[] w1 = word1.toCharArray();
        char[] w2 = word2.toCharArray();
        int m = word1.length(), n = word2.length();

        // 创建DP数组,dp[i][j]表示将word1的前i个字符转换为word2的前j个字符所需的最少操作数
        int[][] dp = new int[m + 1][n + 1];

        // 初始化边界情况:word1的前i个字符转换为空字符串需要i步操作(全删除)
        for (int i = 1; i <= m; i++) {
            dp[i][0] = i;
        }

        // 初始化边界情况:空字符串转换为word2的前j个字符需要j步操作(全插入)
        for (int i = 1; i <= n; i++) {
            dp[0][i] = i;
        }

        // 填充DP数组
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (w1[i - 1] == w2[j - 1]) {
                    // 如果当前字符匹配,不需要额外操作
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    // 如果当前字符不匹配,选择三种操作中的最小值(插入、删除、替换)
                    dp[i][j] = Math.min(dp[i - 1][j] + 1, // 删除操作
                            Math.min(dp[i][j - 1] + 1, // 插入操作
                                    dp[i - 1][j - 1] + 1)); // 替换操作
                }
            }
        }

        // 返回将word1转换为word2所需的最少操作数
        return dp[m][n];
    }

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

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

相关文章

软考高级里《系统架构设计师》容易考吗?

我还是22年通过的架构考试。系统架构设计师属于软考高级科目&#xff0c;难度比初级和中级都要大&#xff0c;往年的通过率也比较低&#xff0c;一般在10-20%左右。从总体来说&#xff0c;这门科目确实是不好过的&#xff0c;大家如果想要备考系统架构设计师的话&#xff0c;还…

营销宝典:4P理论全方位解读,营销人的必修课

这么说吧&#xff0c;4P是营销的一切&#xff0c;也是一切的营销&#xff01; 4P理论产生于20世纪60年代的美国&#xff0c;随着营销组合理论的提出而出现的。 1953年&#xff0c;尼尔博登&#xff08;NeilBorden&#xff09;在美国市场营销学会的就职演说中创造了“市场营销…

【Python】已解决:SyntaxError: invalid character in identifier

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;SyntaxError: invalid character in identifier 一、分析问题背景 在Python编程中&#xff0c;SyntaxError: invalid character in identifier是一个常见的编译…

java Web 优秀本科毕业论文系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 优秀本科毕业论文系统是一套完善的web设计系统&#xff0c;对理解JSP java serlvet 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&a…

苍穹外卖--导入分类模块功能代码

把各层代码拷贝到所需文件夹下&#xff0c; 进行编译 在运行 提交和推送仓库

教程系列1 | 趋动云『社区项目』极速部署 SD WebUI

在上周&#xff0c;趋动云新推出的『社区项目』功能&#xff0c;以“一键克隆”的极致便捷与“省时省力”的高效体验&#xff0c;赢得了广大用户的关注。 随后&#xff0c;启动趋动云『社区项目』教程系列&#xff0c;旨在从零开始&#xff0c;全方位、手把手地引领您深入探索…

【鸿蒙学习笔记】Stage模型

官方文档&#xff1a;Stage模型开发概述 目录标题 Stage模型好处Stage模型概念图ContextAbilityStageUIAbility组件和ExtensionAbility组件WindowStage Stage模型-组件模型Stage模型-进程模型Stage模型-ArkTS线程模型和任务模型关于任务模型&#xff0c;我们先来了解一下什么是…

【Python】一文向您详细介绍 np.inner()

【Python】一文向您详细介绍 np.inner() 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&#xff0c;曾…

突破传统,实时语音技术的革命。Livekit 开源代理框架来袭

🚀 突破传统,实时语音技术的革命!Livekit 开源代理框架来袭! 在数字化时代,实时通信已成为我们日常生活的一部分。但你是否曾想象过,一个能够轻松处理音视频流的代理框架,会如何改变我们的沟通方式?今天,我们就来一探究竟! 🌟 什么是 Livekit 代理框架? Live…

【微信小程序开发】微信小程序界面弹窗,数据存储相关操作代码逻辑实现

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【Stable Diffusion】(基础篇三)—— 关键词和参数设置

提示词和文生图参数设置 本系列笔记主要参考B站nenly同学的视频教程&#xff0c;传送门&#xff1a;B站第一套系统的AI绘画课&#xff01;零基础学会Stable Diffusion&#xff0c;这绝对是你看过的最容易上手的AI绘画教程 | SD WebUI 保姆级攻略_哔哩哔哩_bilibili 本文主要讲…

tk Text文本框赋值,清空

import tkinter as tk# 创建主窗口 root tk.Tk() root.title("文本框内容赋值示例")# 创建一个Text小部件 text_area tk.Text(root, height10, width50) text_area.pack()# 将内容赋值给Text小部件 text_area.insert(tk.END, "这是文本框中的内容。\n")#…

【多线程】wait()和notify()

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 为什么需要wait()方法和notify()方法&#xff1f;2. wait()方法2.1 wait()方法的作用2.2 wait()做的事情2…

App UI性能测试 - PerfDog使用全教程

App 性能测试指标: 响应、内存、CPU、FPS、GPU渲染、耗电、耗流等。 PerfDog的性能数据更加全面,所以下面以PerfDog来介绍安装使用流程及测试数据的获取与分析。 官网: PerfDog | 全平台性能测试分析专家 第一步,先访问官网进行注册, 注册好账号后,点击下载PerfDog,下…

微信公众平台、公众号、小程序联动

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 微信公众平台、公众号、小程序联动 如何通过unionid获取到微信公众openid如何根据code获取微信公…

UNDO 表空间使用率高 active段占用高 无对应事务执行

UNDO表空间使用率告警&#xff0c;查看占用情况 active段占比很高 select tablespace_name,status,sum(bytes/1024/1024) mb from dba_undo_extents group by tablespace_name,status;不同状态的含义&#xff1a;**ACTIVE **&#xff1a;有活动事务在使用 Undo&#xff0c;这…

掌握Qt的QThread:深入多线程编程

​ &#x1f60e; 作者介绍&#xff1a;欢迎来到我的主页&#x1f448;&#xff0c;我是程序员行者孙&#xff0c;一个热爱分享技术的制能工人。计算机本硕&#xff0c;人工制能研究生。公众号&#xff1a;AI Sun&#xff08;领取大厂面经等资料&#xff09;&#xff0c;欢迎加…

产品推荐| 立錡低耗电器件:线性稳压器、Buck 和 Boost 转换器

想让电池用得更久、利用好它的每一份电力&#xff1f;低静态电流的电源转换器是你的必然选择。立錡深谙电源管理之道&#xff0c;为你备好了低耗电的各种产品&#xff0c;其中包括低压差线性稳压器、Buck 转换器和 Boost 转换器&#xff0c;最低消耗仅有 360nA&#xff0c;是无…

前端面试题35(在iOS和Android平台上,实现WebSocket协议有哪些常见的库或框架?)

在iOS和Android平台上&#xff0c;实现WebSocket协议有许多成熟且被广泛使用的库和框架。下面是一些推荐的选项&#xff1a; iOS 平台 SocketRocket 简介&#xff1a;这是由Facebook开源的库&#xff0c;专门为iOS和Mac OS X设计&#xff0c;提供WebSocket连接的功能。它基于S…

Doris数据库---建表、调整表结构操作

一、简介 本文章主讲创建 Doris 自维护的表的语法&#xff0c;以下为本人最近为数据中台接入doris所踩的坑及其解决方案&#xff0c;欢迎点评。 二、doris建表语法&#xff1a; 官网建表语法网址链接&#xff1a;CREATE-TABLE - Apache Doris 官网建表语法如图所示&#xf…