动态规划经典题:编辑距离(hard) 详解,看了还不会你来砍我

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述


为了更好的理解,我们从易到难的来解决编辑距离的问题

一、最长公共子序列

Leetcode最长公共子序列
在这里插入图片描述
一般的,我们在做序列DP问题的时候,遇到两个字符串都会用一个二维数组来进行DP,最长公共子序列简称LCS(longest common subsequence)
对于本题
状态定义:
DP[i][j]表示具有word1[0:i]的字符串和word2[0:j]的字符串中的LCS
比如"abcde" [0:2]也就是abc

状态转移方程:
①当word1[i-1]==word2[j-1]
(因为二维DP中dp[i][0]dp[0][j]代表某个字符串为空,所以要多留一个空间的位置,也就是说dp中的i也就是word中的i-1;或者也可以word1=“”+word1,多一个位置)
那么此时就要从之前的LCS的长度+1
dp[i][j]=dp[i-1][j-1]+1

②当word1[i-1]!=word2[j-1]
此时,就需要找之前的最大公共子序列长度
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
(其实这里容易想到还有一个dp[i-1][j-1],但是此位置已经被找过,再加上的话就多余了,虽然加上没什么问题)

初始化:
当某个字符串为空的时候,LCS=0;
dp[i][0]和dp[0][j]都是0

int longestCommonSubsequence(string text1, string text2) {
        //dp[i][j] 表示的是当前下的最大公共子序列长度
		//text1[0:i]表示text1的0~i这个长度的子串
        text1=" "+text1;
        text2=" "+text2;
        int m = text1.size(),n=text2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        //dp[0][j]表示空串与text2的子序列,为0  dp[i][0]同理
        for(int i=1;i<m;i++)
        {
            for(int j=1;j<n;j++)
            {
                if(text1[i]==text2[j])//两个相等,则最长子序列+1
                {
                    dp[i+1][j+1] = dp[i][j] +1;
                }
                else //不等则是取其中的最大值,因为dp[i][j]表示到此为止的最大子序列
                {
                    dp[i+1][j+1] = max(dp[i+1][j],dp[i][j+1]);
                }
            }
        }
        
        return dp[m][n];
    }

二、两个字符串的删除操作

其实编辑距离就是该题的延伸,所以这道题其实是很重要的
Leetcode两个字符串的删除操作
在这里插入图片描述

思路一:
我们可以转换为求LCS,然后因为这道题只允许删除,所以返回每个字符串的长度和LCS的差值就可以了

int minDistance(string word1, string word2) {
        int m =word1.size();
        int n=word2.size();
        //最长子序列长度
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        //dp的i就是word的i-1
        for(int i=1;i<m+1;i++)
        {
            for(int j=1;j<n+1;j++)
            {
                if(word1[i-1]==word2[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else 
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        return m-dp[m][n]+n-dp[m][n];
    }

思路二:
状态定义:
dp[i][j]表示word1[0:i]和word2[0:j]两个字符串的最少删除数

状态转移方程:
提示:因为二维的DP数组会有多的一个判断空字符串的行和列,所以word中的i-1就是dp中的i
①word1[i-1]==word2[j-1]
那么不需要删除
dp[i][j]=dp[i-1][j-1];

②word1[i-1]!=word2[j-1]
那么就需要删除
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1;
因为比我少一个字符的字符串的最少删除次数以及确定了,我再+1即可

初始化:
当某个字符串为空时 ,删除次数就等于不为空的字符串长度
dp[i][0]=i;
dp[0][j]=j;

int minDistance(string word1, string word2) {
        int m =word1.size();
        int n=word2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        for(int i=0;i<m+1;i++) dp[i][0]=i;
        for(int j=0;i<n+1;i++) dp[0][j]=j;
        
        for(int i=1;i<m+1;i++)
        {
            for(int j=1;j<n+1;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])+1;
                }
            }
        }
        return dp[m][n];
    }

三、编辑距离Hard

Leetcode编辑距离
在这里插入图片描述
其实最难的就是dp的定义,dp数组中的值代表什么意思,然后分析好有什么情况,写出状态转移方程基本上就做出来了

状态定义:
dp[i][j]表示word1[0:i]和word2[0:j]这两个字符串的最少编辑次数

状态转移方程:
通过分析,我们发现每个字符串有三种操作 插入/删除/替换
那么,对于两个字符串 A B 就有六种操作
但是,很快发现:
1.对于A的插入其实就相当于对于B的删除
比如A:dog B:doge
2.对于A的删除其实就相当于对于B的插入
比如A:doge B:dog
3.对于A的替换和对于B的替换是等价的

所以我们分析出
①当word1[i-1]==word2[j-1]
那么此时我们不需要进行操作
dp[i][j]=dp[i-1][j-1];
②当word1[i-1]!=word2[j-1]
那么此时就有三种可能
第一种:对A插入/对B删除
第二种:对A删除/对B插入
第三种:对A替换/对B替换
dp[i][j]=max({dp[i][j-1],dp[i-1][j],dp[i-1][j-1]})+1
dp[i-1][j]就表示对A进行插入/对B删除,因为A少一个
同理dp[i][j-1]也是一样
dp[i-1][j-1]表示替换,因为你必须是同时增加一个字符才是替换,不然的话长度不对,只能是在上一个的基础上插入或者删除

初始化:
当有一个串为空串,那么
dp[i][0]=i ;
dp[0][j]=j ;

int minDistance(string word1, string word2) {
        //序列dp的套路,两个字符串一般用二维dp
        int m = word1.size();
        int n=word2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));//多开一个的原因是dp[i][0]和dp[0][j]有一个串是空串
        for(int i=0;i<m+1;i++) 
            dp[i][0]=i;
        for(int j=0;j<n+1;j++) 
            dp[0][j]=j;
        //初始化两个串某一个为空的操作
        for(int i=1;i<m+1;i++)
        {
            for(int j=1;j<n+1;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-1][j],dp[i][j-1]})+1;
                }
            }
        }
        return dp[m][n];
    }

在这里插入图片描述

  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是Java代码,实现编辑距离动态规划算法,并记录编辑路径: public class EditDistance { public static void main(String[] args) { String word1 = "horse"; String word2 = "ros"; int distance = minDistance(word1, word2); System.out.println("编辑距离为:" + distance); } public static int minDistance(String word1, String word2) { int m = word1.length(); int n = word2.length(); int[][] dp = new int[m + 1][n + 1]; for (int i = 0; i <= m; i++) { dp[i][0] = i; } for (int j = 0; j <= n; j++) { dp[0][j] = j; } for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (word1.charAt(i - 1) == word2.charAt(j - 1)) { dp[i][j] = dp[i - 1][j - 1]; } else { int insert = dp[i][j - 1] + 1; int delete = dp[i - 1][j] + 1; int replace = dp[i - 1][j - 1] + 1; int min = Math.min(insert, Math.min(delete, replace)); dp[i][j] = min; } } } int i = m; int j = n; StringBuilder sb = new StringBuilder(); while (i > 0 && j > 0) { if (word1.charAt(i - 1) == word2.charAt(j - 1)) { sb.append("不变:").append(word1.charAt(i - 1)).append("\n"); i--; j--; } else { int insert = dp[i][j - 1]; int delete = dp[i - 1][j]; int replace = dp[i - 1][j - 1]; int min = Math.min(insert, Math.min(delete, replace)); if (min == insert) { sb.append("插入:").append(word2.charAt(j - 1)).append("\n"); j--; } else if (min == delete) { sb.append("删除:").append(word1.charAt(i - 1)).append("\n"); i--; } else { sb.append("替换:").append(word1.charAt(i - 1)).append("->").append(word2.charAt(j - 1)).append("\n"); i--; j--; } } } while (i > 0) { sb.append("删除:").append(word1.charAt(i - 1)).append("\n"); i--; } while (j > 0) { sb.append("插入:").append(word2.charAt(j - 1)).append("\n"); j--; } System.out.println(sb.reverse().toString()); return dp[m][n]; } } 输出结果为: 替换:e->s 不变:r 替换:o->o 删除:h 删除:r 编辑距离为:3 编辑路径记录了从 word1 到 word2 的最小编辑距离所需的操作,包括插入、删除和替换。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪皮兄弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值