代码随想录算法训练营第五十四天 | LeetCode 392. 判断子序列、115. 不同的子序列

简介: 代码随想录算法训练营第五十四天 | LeetCode 392. 判断子序列、115. 不同的子序列

代码随想录算法训练营第五十四天 | LeetCode 392. 判断子序列、115. 不同的子序列

文章链接:判断子序列不同的子序列

视频链接:判断子序列不同的子序列

1. LeetCode 392. 判断子序列

1.1 思路

  1. 本题是给两个字符串让我们判断字符串 s 是不是字符串 t 的子序列。子序列要求不能改变数组顺序,不要求连续,其实这题和1143. 最长公共子序列是一样的,本题中如果两个字符串的最长公共子序列长度是字符串 s 的长度的话,那么字符串 s 就应该是字符串 t 的子序列了。本题也算是编辑距离的入门题
  2. dp 数组及其下标的含义:用一个二维数组就可以表示出两个字符串里每一个元素它的是否相同的情况,dp[i][j]:以 i-1 为结尾的字符串 s 和以 j-1 为结尾的字符串 t 的相同子序列的长度为 dp[i][j],如果这个长度就是字符串 s 的长度,那字符串 s 就是字符串 t 的子序列。这里定义 i-1 和 j-1 是为了避免对第一行和列进行复杂的初始化,定义为 i 和 j 也行,就是麻烦些
  3. 递推公式:有两个情况,判断的就是两个元素是否相同。if(s[i-1]==t[j-1])为什么判断的是 i-1 和 j-1,因为我们 dp 数组就是这么定义的。如果相等 dp[i][j]=dp[i-1][j-1]+1,含义就是这两个元素相等了,就在前面的基础上加 1。如果不相等 else 只能在 t 中删除元素,即如果 s 的 i-1 位置和 t 的 j-1 位置不相等了,就把 t 的 j-1 位置删除掉了,考虑的就是 t 的 j-2 前面的字符串和 s 的 i-1 前面的字符串的相同子序列长度了,也就是 dp[i][j-1],dp[i][j]=dp[i][j-1],其实可以理解为继承的意思

  4. dp 数组的初始化:根据递推公式可以看出推导方向,根据这个推导方向,就需要初始化第一行 dp[0][j] 和第一列 dp[i][0],根据 dp 数组的含义,这里是 0 的话是需要以 -1 为结尾的相同子序列也就是空串的相同子序列长度,也就是 0 了,因此 dp[i][0]=dp[0][j]=0,其余下标根据递推公式会被覆盖的,因此可以不管,默认为 0 即可
  5. 遍历顺序:根据推导方向,一定要从左到右从上到下,避免赋的是空值 for(int i=1;i<=s.length();i++)for(int j=1;j<=t.length();j++)为什么从 1 开始,因为 0 已经初始化了。为什么要取等于号,因为 dp 数组是从 i-1 和 j-1 开始,不取等于号会漏了最后一个状态,而我们最初定义 dp 数组会是 [s.length()+1][t.length()+1]。最终结果是在 dp[s.length()][t.length()],如果和 s.length()相等就是 true,反之 false
  6. 打印 dp 数组:用于 debug

1.2 代码

class Solution {
    public boolean isSubsequence(String s, String t) {
        int length1 = s.length(); int length2 = t.length();
        int[][] dp = new int[length1+1][length2+1];
        for(int i = 1; i <= length1; i++){
            for(int j = 1; j <= length2; j++){
                if(s.charAt(i-1) == t.charAt(j-1)){
                    dp[i][j] = dp[i-1][j-1] + 1;
                }else{
                    dp[i][j] = dp[i][j-1];
                }
            }
        }
        if(dp[length1][length2] == length1){
            return true;
        }else{
            return false;
        }
    }
}

2. LeetCode 115. 不同的子序列

2.1 思路

  1. 本题求的是子序列,不要求连续,问的是 s 有多少个 t 这样的子序列,其实就是相当于问 s 可以有多少种删除元素的方式。
  2. dp 数组及其下标的含义:对于两个字符串比较里面的元素是否相同的情况是定义二维数组,dp[i][j]:以 i-1 为结尾的 s 中有以 j-1 为结尾的 t 的个数为 dp[i][j]。为什么定义 i-1 和 j-1 上面解释过了
  3. 递推公式:if(s[i-1]==t[j-1])为什么判断的是 i-1 和 j-1,看看 dp 数组的含义,如果相等就不需要考虑这两个字母,而是 i-2 和 j-2 为结尾的个数即 dp[i-1][j-1],那也可以考虑不使用 s[i-1],为什么呢?举例:s=bagg,t=bag,如果不使用 s[3] 此时前面的也可以等于 t,这种情况不使用它就相当于把 s[i-1] 删除掉了,此时的情况就是 dp[i-1][j]。而我们这里不求 t 有多少个 s,因此不能不考虑 t[j-1]。那因此 dp[i][j] 就应该是考虑 s[i-1] 和不考虑 s[i-1] 的情况相加,dp[i][j]=dp[i-1][j-1]+dp[i-1][j]。如果 s[i-1]!=t[j-1] 就是把 s[i-1] 删除的情况,就是不考虑它了,就 dp[i][j]=dp[i-1][j]。

  4. dp 数组的初始化:根据递推公式可以看出推导方向,因此最上方的第一行和第一列一定要初始化,dp[0][j] 第一行这种情况就是以-1 为结尾的空字符串 s 有以 j-1 为结尾的 t 的个数,空字符串肯定没有 t,因此初始化为 0;dp[i][0] 第一列这种情况就是以 i-1 为结尾的 s 有以-1 为结尾的空字符串 t 的个数,那就应该是 1 了,s 肯定有一个空字符串,因此初始化为 1,而 dp[0][0] 也就是这种情况了,也初始化为 1
  5. 遍历顺序:根据推导方向从左到右从上到下,因此 for(int i=1;i<=s.length();i++)for(int j=1;j<=t.length();j++),为什么取 1 以及为什么要等于号上面解释过了。最终结果是在 dp[s.length()][t.length()]
  6. 打印 dp 数组:用于 debug

2.2 代码

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()];
    }
}
相关文章
|
1月前
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
38 0
|
10天前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
13天前
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
|
22天前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
25 3
|
21天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
1月前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
1月前
|
算法
每日一道算法题(Leetcode 20)
每日一道算法题(Leetcode 20)
26 2
|
1月前
|
搜索推荐
插入排序算法的讲解和代码
【10月更文挑战第12天】插入排序是一种基础的排序算法,理解和掌握它对于学习其他排序算法以及数据结构都具有重要意义。你可以通过实际操作和分析,进一步深入了解插入排序的特点和应用场景,以便在实际编程中更好地运用它。
|
27天前
|
缓存 分布式计算 监控
优化算法和代码需要注意什么
【10月更文挑战第20天】优化算法和代码需要注意什么
18 0
|
1月前
|
算法 Java 测试技术
数据结构 —— Java自定义代码实现顺序表,包含测试用例以及ArrayList的使用以及相关算法题
文章详细介绍了如何用Java自定义实现一个顺序表类,包括插入、删除、获取数据元素、求数据个数等功能,并对顺序表进行了测试,最后还提及了Java中自带的顺序表实现类ArrayList。
22 0
下一篇
无影云桌面