动态规划 编辑距离问题 三题

编辑距离问题操作流程

  1. eg: 从 s a t u变为s u 需要经过两次修改或者两次插入或者三次删除操作  

  2. 修改 :从左上方下来      插入:从左边过来     修改 :从上面下来 

4736681746ec4de3b6742009d6cff528.png

 

 

115.不同的子序列(动态规划,编辑距离问题)

115.不同的子序列

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

class Solution {
public:
    int numDistinct(string s, string t) {
        //dp为以i-1为末尾的s中有以j-1为末尾的t字符串的个数 = 删除s中的字符 有多少种方法可以得到t
        vector<vector<uint64_t>> dp(s.size()+1,vector<uint64_t>(t.size()+1,0));
        //初始化
        //t为空字符串时 dp的个数
        for(int i=0;i<s.size();i++){
            dp[i][0]=1;
                                             //dp[0][0]应该是1,空字符串s,可以删除0个元素,变成空字符串t。
        }//s为空字符串时 dp的个数
        for(int j=1;j<t.size();j++){//所以这里要从1开始
            dp[0][j]=0;
        }
        for(int i=1;i<=s.size();i++){
            for(int j=1;j<=t.size();j++){
                if(s[i-1]==t[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.size()][t.size()];
     

    }
};

1.dp的含义

dp是一个二维向量,用于存储动态规划的状态。

  1. vector<vector<uint64_t>> dp:这声明了一个二维向量dp,其中的元素是uint64_t类型的。uint64_t是一个无符号64位整数类型,它可以存储非常大的正整数,这通常用于避免在计算过程中整数溢出。

  2. 这里dp[i][j]将存储字符串s的前i个字符中包含字符串t的前j个字符作为子序列的不同方式的数量

2.dp数组的初始化

for(int i=0;i<s.size();i++){
            dp[i][0]=1;
                                             //dp[0][0]应该是1,空字符串s,可以删除0个元素,变成空字符串t。
        }//s为空字符串时 dp的个数
        for(int j=1;j<t.size();j++){//所以这里要从1开始
            dp[0][j]=0;
        }

 

在初始化dp数组时,dp[0][j]代表的是空字符串st的前j个字符的匹配方式数量。因为空字符串无法匹配任何非空字符串,所以当t非空时,dp[0][j]应该为0。所以,初始化dp[0][j]为0是正确的。

从1开始初始化的原因是因为dp[0][0]已经被初始化为1,代表两个空字符串的匹配方式数量。dp[0][0]是一个特殊情况,代表没有任何字符需要匹配,只有一种方式,即不做任何操作。

对于j的循环,从1开始是为了跳过dp[0][0]这个已经初始化的值,从t的第一个字符开始初始化dp数组。这样,对于t中的每个字符,我们都可以将其对应的dp[0][j]设置为0,因为空字符串s无法匹配任何t中的字符。

3.递推方程

 if(s[i-1]==t[j-1]){
                    dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
                }else{
                    dp[i][j]=dp[i-1][j];
                }详细解释一下
  1. if(s[i-1] == t[j-1]):这个条件检查s的第i个字符(从1开始计数,所以是s[i-1])是否等于t的第j个字符(同样是t[j-1])。如果它们相等,这意味着我们可以使用这个匹配来构建一个有效的子序列。

  2. dp[i][j] = dp[i-1][j-1] + dp[i-1][j]:如果s[i-1]t[j-1]相等,那么dp[i][j]的值由两部分组成:

    • dp[i-1][j-1]:这部分代表了当我们不使用s的第i个字符时,即只考虑s的前i-1个字符时,能够形成t的前j-1个字符的子序列的数量。因为我们现在有一个匹配的字符,所以这些子序列现在可以通过添加s[i-1]来扩展为匹配t的前j个字符的子序列。
    • dp[i-1][j]:这部分代表了当我们使用s的第i个字符时,即考虑s的前i个字符时,能够形成t的前j个字符的子序列的数量。 这部分是在s的第i个字符和t的第j个字符不匹配时计算的,但是由于我们现在有一个匹配,所以这些子序列也可以通过使用这个匹配来增加数量。
  3. else如果s[i-1]t[j-1]不相等,那么我们不能使用s的第i个字符来构建匹配t的第j个字符的子序列。因此,dp[i][j]的值就等于只考虑s的前i-1个字符时(不考虑=删除),能够形成t的前j个字符的子序列的数量,即dp[i-1][j]

 

 

583.两个字符串的删除操作(动态规划,编辑距离问题)

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

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

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

 

示例 1:

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

示例  2:

输入:word1 = "leetcode", word2 = "etco"
输出:4
class Solution {
public:
    int minDistance(string word1, string word2) {
        //dp是删除word1或word2所需要的最少步数
        vector<vector<int>> dp(word1.size()+1,vector<int>(word2.size()+1));
        for(int i=0;i<=word1.size();i++){
            dp[i][0]=i;
        }
        for(int j=0;j<=word2.size();j++){
            dp[0][j]=j;
        }
        for(int i=1;i<=word1.size();i++){
            for(int j=1;j<=word2.size();j++){
                if(word1[i-1]==word2[j-1]){
                    dp[i][j]=dp[i-1][j-1];
                }else{
                     //选择删除1字符串和删除2字符串所得到的最小次数
                    dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1);
                }
            }
           
        } 
        return dp[word1.size()][word2.size()];
    }
};

1.dp数组的含义

//dp是删除word1或word2所需要的最少步数

 

 

2.dp数组的初始化

1.    dp[i][0]代表着将字符串 word1[i] 删除成为 空字符串 的最小次数

2.    dp[0][j]代表着将字符串 word2[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;
        }

 

3.递推方程

两个嵌套的for循环用于填充动态规划数组dp,以计算将字符串word1转换为字符串word2所需的最小编辑距离。这里的dp[i][j]表示将word1的前i个字符转换为word2的前j个字符所需的最小操作次数。

循环中的ij变量分别对应于word1word2的当前字符索引。由于dp数组的大小比输入字符串的长度大1,因此ij的取值范围是1word1.size()1word2.size(),包括边界。

循环体中的代码执行以下操作:

  1. 检查word1的第i个字符(word1[i-1])是否等于word2的第j个字符(word2[j-1]。如果是,则不需要进行任何操作,dp[i][j]的值将与dp[i-1][j-1]相同,即当前两个字符之前的子字符串的编辑距离。

  2. 如果word1的第i个字符不等于word2的第j个字符,则需要考虑三种可能的操作:插入、删除和替换。在这种情况下,dp[i][j]的值将是以下三种情况中的最小值加1:

    • word1中删除第i个字符,然后比较word1的前i-1个字符和word2的前j个字符,即dp[i-1][j] + 1
    • word1中插入一个字符以匹配word2的第j个字符(向word1插入字符等于删除word2中的字符),然后比较word1的前i个字符和word2的前j-1个字符,即dp[i][j-1] + 1
for(int i=1;i<=word1.size();i++){
            for(int j=1;j<=word2.size();j++){
                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);
                }
            }
           
        } 

 

72.编辑距离(动态规划,编辑距离问题)

72.编辑距离

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

 

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

示例 2:

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
class Solution {
public:
    int minDistance(string word1, string word2) {
        vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1));
        for (int i = 0; i <= word1.size(); i++)
            dp[i][0] = i;
        for (int j = 0; j <= word2.size(); j++)
            dp[0][j] = j;
        for (int i = 1; i <= word1.size(); i++) {
            for (int j = 1; j <= word2.size(); j++) {
                if (word1[i - 1] == word2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] =
                        min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}) + 1;
                }
            }
        }
        return dp[word1.size()][word2.size()];
    }
};

dp是一个二维向量,用于存储最小编辑距离的值。dp[i][j]表示将word1的前i个字符转换为word2的前j个字符所需的最小操作次数。

代码的逻辑如下:

  1. 初始化dp数组的第一行和第一列,分别对应于将一个字符串转换为空字符串的情况。这可以通过删除操作来实现,因此dp[i][0]dp[0][j]分别被设置为ij

  2. 使用两个嵌套的for循环遍历word1word2的字符。如果当前字符相同,则dp[i][j]的值与dp[i-1][j-1]相同,因为不需要进行任何操作。

  3. 如果当前字符不同,则需要考虑插入、删除和替换操作。在这种情况下,dp[i][j]的值是dp[i-1][j]dp[i][j-1]dp[i-1][j-1]中的最小值加上1。这三种情况分别对应于删除word1的当前字符、插入word2的当前字符和替换word1的当前字符。

  4. 最终,dp[word1.size()][word2.size()]的值将是将整个word1转换为整个word2所需的最小编辑距离。

 

  • 38
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划可以用来求解编辑距离问题编辑距离是指将一个字符串转换为另一个字符串所需的最小字符操作次数。字符操作包括删除一个字符、插入一个字符和将一个字符替换为另一个字符。 为了求解编辑距离,我们可以使用一个二维数组dp,其中dp[i][j]表示将字符串A的前i个字符转换为字符串B的前j个字符的最优编辑距离。 我们可以使用以下状态转移方程来计算dp数组的值: 1. 当i=0时,dp[j]表示将空字符串转换为字符串B的前j个字符的最优编辑距离,即插入操作的次数,所以dp[j]=j。 2. 当j=0时,dp[i]表示将字符串A的前i个字符转换为空字符串的最优编辑距离,即删除操作的次数,所以dp[i]=i。 3. 当A[i-1]=B[j-1]时,这两个字符相等,不需要进行任何操作,所以dp[i][j]=dp[i-1][j-1]。 4. 当A[i-1]!=B[j-1]时,可以进行三种操作:替换操作、插入操作和删除操作。dp[i][j]可以取这三种操作的最小值,即dp[i][j]=min(dp[i-1][j-1], dp[i][j-1], dp[i-1][j])+1。 最后,dp[m][n]即为将字符串A转换为字符串B的最优编辑距离,其中m和n分别表示字符串A和字符串B的长度。 以下是使用动态规划解决编辑距离问题的示例代码: ```cpp string a = "sfdqxbw"; string b = "gfdgw"; int dp[MAXN][MAXN]; void solve() { int i, j; for (i = 1; i <= a.length(); i++) dp[i][0 = i; for (j = 1; j <= b.length(); j++) dp = j; for (i = 1; i <= a.length(); i++) { for (j = 1; j <= b.length(); j++) { if (a[i - 1 == b[j - 1]) dp[i][j = dp[i - 1][j - 1]; else dp[i][j = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1; } } } ``` 以上是使用动态规划求解编辑距离问题的方法。通过计算dp数组,我们可以得到将字符串A转换为字符串B的最小字符操作次数。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* *2* [动态规划法求解编辑距离问题](https://blog.csdn.net/weixin_42729072/article/details/105160948)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值