• 子序列 --- 编辑距离


    392. 判断子序列

    题目

    给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
    字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
    示例:
    在这里插入图片描述

    思路

    1.确定dp数组(dp table)以及下标的含义
    dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。
    2.确定递推公式
    if (s[i - 1] == t[j - 1]) -----> t中找到了一个字符在s中也出现了—> dp[i][j] = dp[i - 1][j - 1] + 1;
    if (s[i - 1] != t[j - 1]) -----> 相当于t要删除元素,继续匹配 -------> dp[i][j] = dp[i][j - 1]
    3.初始化
    出dp[i][j]都是依赖于dp[i - 1][j - 1] 和 dp[i][j - 1],所以dp[0][0]和dp[i][0]是一定要初始化的。
    dp[0][0] = 0;
    dp[i][0] 表示以下标i-1为结尾的字符串,与空字符串的相同子序列长度,所以为0.
    4. 确定遍历顺序
    遍历顺序也应该是从上到下,从左到右

    代码

    class Solution {
        public boolean isSubsequence(String s, String t) {
            //dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。
            int len1 = s.length();
            int len2 = t.length();
            int[][] dp = new int[len1+1][len2+1];
            //初始化 dp[0][0] 和 dp[i][0] 均为零,不用拿出来单独初始化
            for(int i = 1; i <= len1; i++){
                for(int j =1; j <= len2; 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];
                    }
                }
            }
            return dp[len1][len2] == len1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    115. 不同的子序列

    题目

    给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中t出现的个数。
    字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
    示例:
    在这里插入图片描述

    思路

    1.确定dp数组(dp table)以及下标的含义
    dp[i][j]:以i-1为结尾的s子序列 中 出现以j-1为结尾的t的个数为dp[i][j]。
    2.确定递推公式
    是要分析两种情况:
    s[i - 1] 与 t[j - 1]相等
    s[i - 1] 与 t[j - 1] 不相等
    当s[i - 1] 与 t[j - 1]相等时,dp[i][j]可以有两部分组成。
    一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。
    一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]
    (1) 当s[i - 1] 与 t[j - 1]相等时,dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
    (2) 当s[i - 1] 与 t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配,即:dp[i - 1][j] dp[i][j] = dp[i - 1][j];
    3.初始化
    dp[i][0] 和dp[0][j]是一定要初始化的。
    dp[i][0] 表示:以i-1为结尾的s可以随便删除元素,出现空字符串的个数。 出现空字符串的个数就是1。
    dp[0][j]:空字符串s可以随便删除元素,出现以j-1为结尾的字符串t的个数。dp[0][j]一定都是0
    4.确定遍历顺序
    一定是从上到下,从左到右

    代码

    class Solution {
        public int numDistinct(String s, String t) {
            //dp[i][j]:以i-1为结尾的s子序列 中 出现以j-1为结尾的t的个数为dp[i][j]。
            int len1 = s.length();
            int len2 = t.length();
            int[][] dp = new int[len1+1][len2+1];
            //初始化:dp[0][j]始终是0
            for(int i = 0; i <= len1; i++){
                dp[i][0] = 1;
            }
            for(int i = 1; i <= len1; i++){
                for(int j = 1; j <= len2; 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[len1][len2];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    583 两个字符串的删除操作

    题目

    给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数。
    每步 可以删除任意一个字符串中的一个字符。
    示例:
    在这里插入图片描述

    思路

    1.确定dp数组(dp table)以及下标的含义
    dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。
    2 确定递推公式
    (1)当word1[i - 1] 与 word2[j - 1]相同的时候,dp[i][j] = dp[i - 1][j - 1];
    (2)当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
    最后当然是取最小值,所以当word1[i - 1] 与 word2[j - 1]不相同的时候,递推公式:dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});
    3.初始化
    dp[i][0] 和 dp[0][j]是一定要初始化的。
    dp[i][0]:word2为空字符串,以i-1为结尾的字符串word1要删除多少个元素,才能和word2相同呢,很明显dp[i][0] = i。
    dp[0][j]的话同理

    for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
    for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
    
    • 1
    • 2

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

    代码

    class Solution {
        public int minDistance(String word1, String word2) {
            //dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。
            int len1 = word1.length();
            int len2 = word2.length();
            int[][] dp = new int[len1+1][len2+1];
            //初始化
            for(int i = 0; i <= len1; i++){
                dp[i][0] = i;
            }
            for(int j = 0; j <= len2; j++){
                dp[0][j] = j;
            }
            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];
                    }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[len1][len2];
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    72. 编辑距离

    题目

    给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
    你可以对一个单词进行如下三种操作:
    插入一个字符
    删除一个字符
    替换一个字符
    示例:
    在这里插入图片描述
    1.确定dp数组(dp table)以及下标的含义
    dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。
    2. 确定递推公式

    if (word1[i - 1] == word2[j - 1])
        不操作  ----->  dp[i][j] = dp[i - 1][j - 1];
    if (word1[i - 1] != word2[j - 1])
        增
        删
        换
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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;

    操作二:添加元素
    **!!! word2添加一个元素,相当于word1删除一个元素,**例如 word1 = “ad” ,word2 = “a”,word1删除元素’d’ 和 word2添加一个元素’d’,变成word1=“a”, word2=“ad”, 最终的操作数是一样!

    操作三:替换元素
    word1替换word1[i - 1],使其与word2[j - 1]相同,此时不用增加元素,那么以下标i-2为结尾的word1 与 j-2为结尾的word2的最近编辑距离 加上一个替换元素的操作。同理,word2替换word2[j - 1],使其与word1[i - 1]相同,也是如下式子🦁
    即 dp[i][j] = dp[i - 1][j - 1] + 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;

    3.初始化
    dp[i][0] 和 dp[0][j]
    dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最近编辑距离为dp[i][0]。
    那么dp[i][0]就应该是i,对word1里的元素全部做删除操作,即:dp[i][0] = i; 同理dp[0][j] = j;
    4.确定遍历顺序

    代码

    class Solution {
        public int minDistance(String word1, String word2) {
            //dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。
            int len1 = word1.length();
            int len2 = word2.length();
            int[][] dp = new int[len1+1][len2+1];
            //初始化
            for(int i = 0; i <= len1; i++){
                dp[i][0] = i;      
            }
            for(int j = 0; j <= len2; j++){
                dp[0][j] = j;       
            }
            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];
                    }else{
                        dp[i][j] = Math.min(Math.min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+1);            
                    }
                }
            }
            return dp[len1][len2];
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    LeetCode 热题 C++ 85. 最大矩形
    【Truffle】四、通过Ganache部署连接
    备考cisp拿证,收藏这一篇就够了
    HK32F030MF4P6 TIM2(定时器例程)
    21-Docker-常用命令详解-docker run
    R语言使用econocharts包创建微观经济或宏观经济图、indifference函数可视化无差异曲线(indifference curve)
    Python语言及其应用
    三维图形程序员必学-CGAL几何算法
    【工具问题】IDEA每次关闭的时候都会弹框显示closing project,然后弹框持续很久就像卡住了
    从元宇宙、地产数字化到呼叫中心,华为云携手伙伴共创新价值
  • 原文地址:https://blog.csdn.net/qq_48094059/article/details/125877908