代码随想录-Day51

115. 不同的子序列

给你两个字符串 s 和 t ,统计并返回在 s 的 子序列 中 t 出现的个数,结果需要对 109 + 7 取模。

示例 1:

输入:s = “rabbbit”, t = “rabbit”
输出:3
解释:
如下所示, 有 3 种可以从 s 中得到 “rabbit” 的方案。
rabbbit
rabbbit
rabbbit
示例 2:

输入:s = “babgbag”, t = “bag”
输出:5
解释:
如下所示, 有 5 种可以从 s 中得到 “bag” 的方案。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
    public int numDistinct(String s, String t) {
        int[][] dp = new int[s.length() + 1][t.length() + 1];
        for (int i = 0; i < s.length() + 1; i++) {
            dp[i][0] = 1;
        }
        
        for (int i = 1; i < s.length() + 1; i++) {
            for (int j = 1; j < t.length() + 1; j++) {
                if (s.charAt(i - 1) == t.charAt(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.length()][t.length()];
    }
}

这段代码是用于解决「不同子序列」问题的Java实现,实质上是计算字符串 t 在字符串 s 中的不同子序列出现的次数。给定两个字符串 st,目标是找出 ts 中有多少种不同的子序列出现方式。

代码解析

  1. 初始化动态规划数组:

    • 创建一个二维数组 dp,其大小为 (s.length() + 1) x (t.length() + 1)dp[i][j] 的值代表 s 的前 i 个字符和 t 的前 j 个字符组成的子序列的出现次数。额外的一列是为了方便边界条件的处理,其中 dp[i][0] 的值默认为1,因为任何字符串都是空字符串的子序列,且只有一种方式。
  2. 动态规划迭代:

    • 从1到 s.length() 遍历 s 的每个字符,从1到 t.length() 遍历 t 的每个字符。
    • 对于 s 的第 i 个字符和 t 的第 j 个字符:
      • 如果两个字符相等,那么 s 的前 i 个字符和 t 的前 j 个字符组成的子序列的出现次数等于 s 的前 i-1 个字符和 t 的前 j-1 个字符组成的子序列的出现次数加上 s 的前 i-1 个字符和 t 的前 j 个字符组成的子序列的出现次数,即 dp[i - 1][j - 1] + dp[i - 1][j]
      • 如果两个字符不相等,那么出现次数等于 s 的前 i-1 个字符和 t 的前 j 个字符组成的子序列的出现次数,即 dp[i - 1][j]
  3. 返回结果:

    • 最后返回 dp[s.length()][t.length()],即 ts 中的不同子序列出现的次数。

时间复杂度和空间复杂度

  • 时间复杂度: O(m * n),其中 m 和 n 分别是字符串 st 的长度。这是因为需要遍历两个字符串的所有可能的字符组合来计算子序列的出现次数。
  • 空间复杂度: O(m * n),需要一个大小为 (m + 1) x (n + 1) 的动态规划数组 dp 来存储中间结果。

总结

这段代码通过动态规划的方法,有效地解决了不同子序列计数问题。尽管时间复杂度和空间复杂度较高,但在许多实际应用场景中,这样的性能通常是可接受的,特别是当字符串长度适中时。如果需要进一步优化空间复杂度,可以考虑使用滚动数组技术,将空间复杂度降低到 O(min(m, n)),但这通常会增加代码的复杂度。在处理此类问题时,权衡时间和空间资源是一个常见的考量因素。

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

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

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

示例 1:

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

输入:word1 = “leetcode”, word2 = “etco”
输出:4
在这里插入图片描述

方法一:

// dp数组中存储word1和word2最长相同子序列的长度
class Solution {
    public int minDistance(String word1, String word2) {
        int len1 = word1.length();
        int len2 = word2.length();
        int[][] dp = new int[len1 + 1][len2 + 1];

        for (int i = 1; i <= len1; i++) {
            for (int j = 1; j <= len2; j++) {
                if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }

        return len1 + len2 - dp[len1][len2] * 2;
    }
}

这段代码是用于解决「编辑距离之最短编辑脚本」问题的Java实现,但其计算方法并不直接针对编辑距离,而是利用了最长公共子序列(LCS, Longest Common Subsequence)的概念。给定两个字符串 word1word2,目标是计算将 word1 转换为 word2 所需的最少编辑操作数,这里的编辑操作包括插入、删除和替换一个字符。

代码解析

  1. 初始化动态规划数组:

    • 创建一个二维数组 dp,其大小为 (len1 + 1) x (len2 + 1)dp[i][j] 的值代表 word1 的前 i 个字符和 word2 的前 j 个字符的最长公共子序列的长度。额外的一列和一行是为了方便边界条件的处理。
  2. 动态规划迭代:

    • 从1到 len1 遍历 word1 的每个字符,从1到 len2 遍历 word2 的每个字符。
    • 对于 word1 的第 i 个字符和 word2 的第 j 个字符:
      • 如果两个字符相等,那么 word1 的前 i 个字符和 word2 的前 j 个字符的最长公共子序列的长度等于 word1 的前 i-1 个字符和 word2 的前 j-1 个字符的最长公共子序列的长度加1,即 dp[i-1][j-1] + 1
      • 如果两个字符不相等,那么最长公共子序列的长度等于 word1 的前 i-1 个字符和 word2 的前 j 个字符的最长公共子序列的长度与 word1 的前 i 个字符和 word2 的前 j-1 个字符的最长公共子序列的长度中的较大值,即 Math.max(dp[i-1][j], dp[i][j-1])
  3. 计算编辑距离:

    • 最长公共子序列的长度可以被视为两个字符串共享的字符数量。为了将 word1 转换成 word2,我们需要删除 word1 中不在公共子序列中的字符,并在适当位置插入 word2 中不在公共子序列中的字符。因此,编辑距离等于 word1word2 的长度之和减去两倍的最长公共子序列的长度,即 len1 + len2 - dp[len1][len2] * 2

时间复杂度和空间复杂度

  • 时间复杂度: O(m * n),其中 m 和 n 分别是字符串 word1word2 的长度。这是因为需要遍历两个字符串的所有可能的字符组合来计算最长公共子序列的长度。
  • 空间复杂度: O(m * n),需要一个大小为 (m + 1) x (n + 1) 的动态规划数组 dp 来存储中间结果。

总结

这段代码通过计算最长公共子序列并利用其长度来间接计算编辑距离,提供了一种有效的解决方案。尽管它并没有直接使用编辑距离的动态规划公式,但通过巧妙地利用最长公共子序列的概念,仍然达到了计算编辑距离的目的。这种间接的方法有时在处理某些变体问题时会更加灵活和直观。如果需要进一步优化空间复杂度,可以考虑使用滚动数组技术,将空间复杂度降低到 O(min(m, n)),但这可能会使代码变得更复杂。

方法二:

// dp数组中存储需要删除的字符个数
class Solution {
    public int minDistance(String word1, String word2) {
        int[][] dp = new int[word1.length() + 1][word2.length() + 1];
        for (int i = 0; i < word1.length() + 1; i++) dp[i][0] = i;
        for (int j = 0; j < word2.length() + 1; j++) dp[0][j] = j;
        
        for (int i = 1; i < word1.length() + 1; i++) {
            for (int j = 1; j < word2.length() + 1; j++) {
                if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                }else{
                    dp[i][j] = Math.min(dp[i - 1][j - 1] + 2,
                                        Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));
                }
            }
        }
        
        return dp[word1.length()][word2.length()];
    }
}

这段代码是用于解决「编辑距离」问题的Java实现,该问题要求计算将字符串 word1 转换为字符串 word2 所需的最小编辑操作数。编辑操作包括插入、删除和替换一个字符。代码通过动态规划方法实现,直接计算最小编辑距离。

代码解析

  1. 初始化动态规划数组:

    • 创建一个二维数组 dp,其大小为 (word1.length() + 1) x (word2.length() + 1)dp[i][j] 的值代表将 word1 的前 i 个字符转换为 word2 的前 j 个字符所需的最小编辑操作数。额外的一列和一行是为了方便边界条件的处理,其中 dp[i][0] 的值初始化为 idp[0][j] 的值初始化为 j,因为将一个字符串转换为空字符串或从空字符串转换为一个字符串所需的操作数分别为字符串的长度。
  2. 动态规划迭代:

    • 从1到 word1.length() + 1 遍历 word1 的每个字符,从1到 word2.length() + 1 遍历 word2 的每个字符。
    • 对于 word1 的第 i 个字符和 word2 的第 j 个字符:
      • 如果两个字符相等,那么将 word1 的前 i 个字符转换为 word2 的前 j 个字符所需的最小编辑操作数等于将 word1 的前 i-1 个字符转换为 word2 的前 j-1 个字符所需的最小编辑操作数,即 dp[i - 1][j - 1]
      • 如果两个字符不相等,那么最小编辑操作数等于以下三种操作中的最小值:
        • word1 的第 i 个字符替换为 word2 的第 j 个字符,加上将 word1 的前 i-1 个字符转换为 word2 的前 j-1 个字符所需的最小编辑操作数加2(因为替换操作成本定义为2)。
        • 删除 word1 的第 i 个字符,加上将 word1 的前 i-1 个字符转换为 word2 的前 j 个字符所需的最小编辑操作数加1。
        • word1 的前 i 个字符后插入 word2 的第 j 个字符,加上将 word1 的前 i 个字符转换为 word2 的前 j-1 个字符所需的最小编辑操作数加1。
  3. 返回结果:

    • 最后返回 dp[word1.length()][word2.length()],即 word1 转换为 word2 所需的最小编辑操作数。

时间复杂度和空间复杂度

  • 时间复杂度: O(m * n),其中 m 和 n 分别是字符串 word1word2 的长度。这是因为需要遍历两个字符串的所有可能的字符组合来计算最小编辑操作数。
  • 空间复杂度: O(m * n),需要一个大小为 (m + 1) x (n + 1) 的动态规划数组 dp 来存储中间结果。

总结

这段代码通过动态规划方法,有效地解决了编辑距离问题,直接计算出将一个字符串转换为另一个字符串所需的最小编辑操作数。如果需要进一步优化空间复杂度,可以考虑使用滚动数组技术,将空间复杂度降低到 O(min(m, n)),但这可能会使代码变得更加复杂。在实际应用中,选择合适的优化策略取决于具体的需求和资源限制。

方法三:

//DP - longest common subsequence (用最長公共子序列反推)
class Solution {
    public int minDistance(String word1, String word2) {
        char[] char1 = word1.toCharArray();
        char[] char2 = word2.toCharArray();

        int len1 = char1.length;
        int len2 = char2.length;

        int dp[][] = new int [len1 + 1][len2 + 1];

        for(int i = 1; i <= len1; i++){
            for(int j = 1; j <= len2; j++){
                if(char1[i - 1] == char2[j - 1])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }

        return len1 + len2 - (2 * dp[len1][len2]);//和leetcode 1143只差在這一行。
    }
}

这段代码是用于解决「编辑距离」问题的Java实现,但它实际上是通过计算两个字符串 word1word2 的最长公共子序列(LCS, Longest Common Subsequence)的长度,然后基于这个信息来间接计算编辑距离。这种方法的思路是,编辑距离可以看作是两个字符串各自去除最长公共子序列部分后,剩下的字符数总和。

代码解析

  1. 初始化动态规划数组:

    • 创建一个二维数组 dp,其大小为 (len1 + 1) x (len2 + 1)dp[i][j] 的值代表 word1 的前 i 个字符和 word2 的前 j 个字符的最长公共子序列的长度。额外的一列和一行是为了方便边界条件的处理。
  2. 动态规划迭代:

    • 从1到 len1 遍历 word1 的每个字符,从1到 len2 遍历 word2 的每个字符。
    • 对于 word1 的第 i 个字符和 word2 的第 j 个字符:
      • 如果两个字符相等,那么 word1 的前 i 个字符和 word2 的前 j 个字符的最长公共子序列的长度等于 word1 的前 i-1 个字符和 word2 的前 j-1 个字符的最长公共子序列的长度加1,即 dp[i-1][j-1] + 1
      • 如果两个字符不相等,那么最长公共子序列的长度等于 word1 的前 i-1 个字符和 word2 的前 j 个字符的最长公共子序列的长度与 word1 的前 i 个字符和 word2 的前 j-1 个字符的最长公共子序列的长度中的较大值,即 Math.max(dp[i-1][j], dp[i][j-1])
  3. 计算编辑距离:

    • 最长公共子序列的长度可以被视为两个字符串共享的字符数量。为了将 word1 转换成 word2,我们需要删除 word1 中不在公共子序列中的字符,并在适当位置插入 word2 中不在公共子序列中的字符。因此,编辑距离等于 word1word2 的长度之和减去两倍的最长公共子序列的长度,即 len1 + len2 - (2 * dp[len1][len2])

时间复杂度和空间复杂度

  • 时间复杂度: O(m * n),其中 m 和 n 分别是字符串 word1word2 的长度。这是因为需要遍历两个字符串的所有可能的字符组合来计算最长公共子序列的长度。
  • 空间复杂度: O(m * n),需要一个大小为 (m + 1) x (n + 1) 的动态规划数组 dp 来存储中间结果。

总结

这段代码通过计算最长公共子序列并利用其长度来间接计算编辑距离,提供了一种有效的解决方案。尽管这种方法并不是直接计算编辑距离的标准动态规划公式,但通过计算最长公共子序列的长度,然后根据这个信息推导出编辑距离,同样能高效地解决问题。这种方法在某些情况下可能更为直观,尤其是在理解最长公共子序列概念的基础上。如果需要进一步优化空间复杂度,可以考虑使用滚动数组技术,将空间复杂度降低到 O(min(m, n)),但这可能会使代码变得更复杂。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于 LlamaIndex、Claude-3.5 Sonnet 和 MongoDB,构建具有超级检索能力的智能体

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、算法项目落地经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 总结链接如…

Day1--每日一练

&#x1f341; 个人主页&#xff1a;爱编程的Tom&#x1f4ab; 本篇博文收录专栏&#xff1a;每日一练-算法篇&#x1f449; 目前其它专栏&#xff1a;c系列小游戏 c语言系列--万物的开始_ Java专栏等 &#x1f389; 欢迎 &#x1f44d;点赞✍评论⭐收藏&…

Java之父James Gosling宣布正式退休 创造无数人的饭碗

编程语言Java的创始人&#xff0c;被誉为“Java之父”的James Gosling&#xff0c;近日在社交媒体上宣布了自己正式退休的消息。Gosling表示&#xff1a;“我终于退休了。做了这么多年的软件工程师&#xff0c;现在是时候享受人生了。”他透露&#xff0c;在亚马逊的过去7年是非…

代码随想录算法训练营第四十七天|1143.最长公共子序列、 1035.不相交的线、53. 最大子序和、392.判断子序列

1143.最长公共子序列 题目链接&#xff1a;1143.最长公共子序列 文档讲解&#xff1a;代码随想录 状态&#xff1a;一开始没想明白为啥要 max(dp[i - 1][j], dp[i][j - 1]) 思路&#xff1a; 如果text1[i - 1] 与 text2[j - 1]相同&#xff0c;那么找到了一个公共元素&#xff…

GitLab介绍,以及add an SSH key

GitLab GitLab 是一个用于仓库管理系统的开源项目&#xff0c;现今并在国内外大中型互联网公司广泛使用。 git,gitlab,github区别 git 是一种基于命令的版本控制系统&#xff0c;全命令操作&#xff0c;没有可视化界面&#xff1b; gitlab 是一个基于git实现的在线代码仓库…

K8s驱逐场景以及规避方案参考 —— 筑梦之路

Pod 驱逐分为两种情况&#xff1a; 较安全驱逐 & 提高稳定性的良性驱逐 API 发起驱逐&#xff0c;典型案例&#xff1a;kubectl drain Node Not Ready 时&#xff0c;Controller Manager 发起的驱逐 有风险的驱逐 节点压力驱逐 节点磁盘空间不足、内存不足 或 Pid 不足&…

简易Qt串口助手

界面显示如下 关于串口类 初始化 设置串口号 设置波特率 打开串口 发送按钮功能实现 接收数据显示在控件中 关闭串口

Vortex GPGPU的硬件设计和代码结构分析

文章目录 前言一、GPGPU是什么&#xff1f;1.1 GPU和GPGPU之间的差异1.2 GPU和CPU之间的集成方式1.3 GPU包含什么&#xff08;列举和VMIPS向量体系结构的差异&#xff09; 二、Vortex GPGPU是什么&#xff1f;2.1 Vortex GPGPU的技术边界和验证环境2.2 Vortex GPGPU的指令集设计…

30万的剧本杀店 被“好色”店长玩死了

文&#xff5c;琥珀食酒社 作者 | 朱珀 对开店搞钱的人来讲 什么才是最苦逼的&#xff1f; 不是一开始生意就不行 而是刚开始好到不行 最后只剩下不行 本期投稿的主人公糊糊 就是这样的 苦逼大BOSS 30万开剧本杀店 短短几个月 从巅峰跌到谷底 被捞钱又好色的猪队友…

C++ 类和对象 拷贝构造函数

一 拷贝构造函数的概念&#xff1a; 拷贝构造函数是一种特殊的构造函数&#xff0c;用于创建一个对象是另一个对象的副本。当需要用一个已存在的对象来初始化一个新对象时&#xff0c;或者将对象传递给函数或从函数返回对象时&#xff0c;会调用拷贝构造函数。 二 拷贝构造函…

LabVIEW高能质子束流密度分布测试系统

LabVIEW平台开发的高能质子束流密度分布测试系统。该系统主要应用于电子器件的抗辐射加固试验&#xff0c;旨在精确测量高能质子束的密度分布&#xff0c;以评估电子器件在辐射环境下的性能表现和耐受能力。 系统组成与设计 硬件组成&#xff1a; 法拉第杯探测器&#xff1a;…

自动化测试高级控件交互方法:TouchAction、触屏操作、点按,双击,滑动,手势解锁!

在自动化测试领域中&#xff0c;TouchAction 是一种非常强大的工具&#xff0c;它允许我们模拟用户在设备屏幕上的各种触摸事件。这种模拟不仅限于简单的点击操作&#xff0c;还包括滑动、长按、多点触控等复杂的手势。 点按与双击 点按和双击是触屏设备上最基本的操作之一。…

数据库图形化管理界面应用 Navicat Premium 使用教程

经同学介绍的一个把数据库可视化的软件Navicat Premium&#xff0c;很好用&#xff0c;在这里分享一下&#xff0c;需要的同学可以去了解看看 一&#xff1a;下载并解压 链接&#xff1a;https://pan.baidu.com/s/1ZcDH6m7EAurAp_QmXWx81A 提取码&#xff1a;e5f6 解压到合…

景芯SoC训练营DFT debug

景芯训练营VIP学员在实践课上遇到个DFT C1 violation&#xff0c;导致check_design_rule无法通过&#xff0c;具体报错如下&#xff1a; 遇到这个问题第一反映一定是确认时钟&#xff0c;于是小编让学员去排查add_clock是否指定了时钟&#xff0c;指定的时钟位置是否正确。 景芯…

Redis原理-数据结构

Redis原理篇 1、原理篇-Redis数据结构 1.1 Redis数据结构-动态字符串 我们都知道Redis中保存的Key是字符串&#xff0c;value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。 不过Redis没有直接使用C语言中的字符串&#xff0c;因为C语言字符串存…

【操作系统】进程管理——进程的同步与互斥(个人笔记)

学习日期&#xff1a;2024.7.8 内容摘要&#xff1a;进程同步/互斥的概念和意义&#xff0c;基于软/硬件的实现方法 进程同步与互斥的概念和意义 为什么要有进程同步机制&#xff1f; 回顾&#xff1a;在《进程管理》第一章中&#xff0c;我们学习了进程具有异步性的特征&am…

Apache AGE中的图

图由一组点和边组成&#xff0c;其中每个节点和边都具有属性映射。点是图的基本对象&#xff0c;可以独立于图中的其他任何对象存在。边创建了两个点之间的有向连接。 创建图 要创建图&#xff0c;可以使用 ag_catalog 命名空间中的 create_graph 函数。 create_graph() 语法…

C++进阶-二叉树进阶(二叉搜索树)

1. 二叉搜索树 1.1 二叉搜索树概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 1.若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值2.若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于…

Jenkins教程-15-常用插件-Blue Ocean

上一小节我们学习了Jenkins定时任务构建的方法&#xff0c;本小节我们讲解一下Jenkins常用插件Blue Ocean的使用方法。 Blue Ocean 提供了一套可视化操作界面来帮助创建、编辑 Pipeline 任务。 Blue Ocean 特性&#xff1a; 流水线编辑器&#xff1a;用于创建贯穿始终的持续交…

一、redis-万字长文读懂redis

高性能分布式缓存Redis `第一篇章`1.1缓存发展史&缓存分类1.1.1 大型网站中缓存的使用带来的问题1.1.2 常见缓存的分类及对比与memcache对比1.2 数据类型选择&应用场景1.2.1 string1.2.2 hash1.2.3 链表1.2.4 set1.2.5 sortedset有序集合类型1.2.6 总结1.3 Redis高级应…