【动态规划】将str1变成str2的最小编辑代价

本文详细讲解了一道动态规划问题,涉及字符串编辑操作(插入、删除、替换)的最少成本计算,通过实例和C++代码展示了如何从空串开始逐步构建最小代价矩阵。关键在于理解递推条件和四种操作路径的选择。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:
在这里插入图片描述
这算是动态规划我弄得十分透彻的一题了。

首先,我们回顾一下做动态规划题目的几个要点。

1.初始化条件,即动态矩阵的初始化

2.递推条件,即dp【i】【j】是怎么得来的

那么我们来分析一下这道题:

首先,动态规划处理字符串的时候,一般都会从空串起手,那么我们这题也从空串开始考虑。

计算上空串,并且将空串作为str1和str2的第一个字符。
那么dp【i】【j】就代表从str1【0…i】->(转变为)str2【0…j】需要的花费

这个花费来自于三个路径

第一个路径:由插入得到。str1【0…i】先编辑成str2【0…j-1】,再由str2【0…j-1】插入到str2【0…j】

dp【i】【j-1 】+ ic

第二个路径,由删除得到。str1【0…i】先编辑成str1【0…i-1】,再由str1【0…i-1】转变为str2【0…j】

即dp【i-1】【j】+dc

第三个路径,由替换得到。
而替换又分为两种情况:

第一种为当前字符串匹配的情况:那么就等于dp【i-1】【j-1】
第二种为当前字符串不匹配的情况:那么久等于dp【i-1】【j-1】+rc

那么,最后dp【i】【j】的值为这四种情况中的最小值。

那么初始化也就很简单了。

第一列为str1->删除变为空串的花费

第一行为str1->插入变为str2的花费

虽然有以上说明了,但是我还是举个例子来帮助大家理解吧。因为我自己看递推关系式的时候也没有很看懂。

比如
str1 = “ab12cd3”
str2 = “abcdf”
插入花费ic = 5
删除花费dc = 3
替换花费rc = 2

刚开始我们将空字符直接放在这两个字符串前面,作为str1【0】和str2【0】

那么第一列就为str1->空字符串的花费,为0 3 6 9 12 15 18 21;
第一行为空字符串->str2的花费 为0 5 10 15 25

那么,我们对于dp【1】【1】即str1 = ‘0a’ str2 = ‘0a’(0代表空字符串)
很容易得出dp【1】【1】为0,因为当前字符相等,属于第二种情况

问题的关键来了,对于dp【1】【2】 str1 = ‘0a’ str2='0ab’的情况呢?

第一个路径,插入操作,为0a->0ab 可以直接得出是0+5 = 5;
第二个路径,删除操作,为0a->0>0ab 从0->0ab是10,那么这里就会是13(因为删掉了a,然后从0->0ab
第三个路径为替换操作,由于当前字符不相等,属于第一种情况,那么就会等于dp【i-1】【j-1】+2 = 7;

那么这个位置的最小值就为5,即dp【i】【j】 = 5;

具体矩阵如下:
在这里插入图片描述
C++代码附带测试:

#include<iostream>
#include<cstring>
#include<Math.h>

using namespace std;


class Solution{
	public:
		int minEditCost(string str1, string str2, int ic, int dc, int rc) {
			//ic 插入一个字符 dc删除一个字符 rc替换一个字符 
        	int m = str1.length()+1;
        	int n = str2.length()+1;
        	
        	//需要留下空字符串的计算空间 
        	int dp[m][n] = {0};
        	
        	//初始化第一列,为str1的各个字符->空字符串的花费,全为删除 
			for(int i=0;i<m;i++){
				dp[i][0] = i*dc;
			} 
			
			//初始化第一行,为空字符串->str2的各个字符,全为插入操作
			for(int i=0;i<n;i++){
				dp[0][i] = i*ic;
			}
			
			//dp[i][j]表示字符串(包括空字符串,且空字符串记作第一个 0)str1[0..i]-st2[0..j]的最少花费 
			for(int i=1;i<m;i++){//遍历需要从1开始,因为空字符已经初始化过了 
				for(int j=1;j<n;j++){
					int dp1 = dp[i][j-1] + ic;//插入操作
					int dp2 = dp[i-1][j] + dc;//删除操作
					int dpx = min(dp1,dp2);
					
					//替换操作
					int dpy = 0;
					if(str1[i]==str2[j]){
						dpy = dp[i-1][j-1];//不需要任何操作 
					}else{
						dpy = dp[i-1][j-1]+rc;//需要一次替换
					}
					dp[i][j] = min(dpx,dpy);//为这四种路线中的最小值
					cout<<dp[i][j]<<" ";
				}
				cout<<endl;
			}
			return dp[m-1][n-1];
};
	private:
};


int main(){
	string str1 = "ab12cd3";
	string str2 = "abcdf";
	Solution solution;
	int cost = solution.minEditCost(str1,str2,5,3,2);
	cout<<endl;
	cout<<"str1->str2需要的最少花费为:"<<cost;
}

### 动态规划实现最小编辑距离 最小编辑距离问题是经典的动态规划问题之一,其目标是找到将一个字符串转换为另一个字符串所需的最少操作次数。这些操作通常包括插入、删除和替换字符。 以下是基于动态规划方法的C语言实现代码: ```c #include <stdio.h> #include <string.h> // 取三个整数中的最小值 int min(int a, int b, int c) { int temp = (a < b) ? a : b; return (temp < c) ? temp : c; } // 计算两个字符串之间的最小编辑距离 int editDistance(char* str1, char* str2) { int len1 = strlen(str1); int len2 = strlen(str2); // 创建二维数组存储中间结果 int dp[len1 + 1][len2 + 1]; // 初始化边界条件 for (int i = 0; i <= len1; i++) { dp[i][0] = i; // 当第二个字符串为空时,需要执行i次删除操作 } for (int j = 0; j <= len2; j++) { dp[0][j] = j; // 当第一个字符串为空时,需要执行j次插入操作 } // 填充dp表 for (int i = 1; i <= len1; i++) { for (int j = 1; j <= len2; j++) { if (str1[i - 1] == str2[j - 1]) { dp[i][j] = dp[i - 1][j - 1]; // 字符相等时不需额外操作 } else { int insertion = dp[i][j - 1] + 1; // 插入操作 int deletion = dp[i - 1][j] + 1; // 删除操作 int replacement = dp[i - 1][j - 1] + 1; // 替换操作 dp[i][j] = min(insertion, deletion, replacement); // 选取最优解 } } } return dp[len1][len2]; } int main() { char str1[100], str2[100]; printf("Enter first string: "); scanf("%s", str1); printf("Enter second string: "); scanf("%s", str2); int result = editDistance(str1, str2); printf("Minimum Edit Distance between '%s' and '%s': %d\n", str1, str2, result); return 0; } ``` #### 解析 该程序的核心在于构建了一个`dp`表格用于记录子问题的结果[^2]。 - `dp[i][j]`表示将`str1`的前`i`个字符转换成`str2`的前`j`个字符所需要的最小编辑距离。 - 边界条件初始化:当其中一个字符串为空时,另一字符串的所有字符都需要被插入或删除。 - 对于每个位置`(i,j)`,如果对应的字符相同,则无需任何操作;否则考虑三种可能的操作(插入、删除、替换),并选择其中代价最小的一种作为最终结果[^3]。 此算法的时间复杂度为O(mn),空间复杂度同样为O(mn),其中m和n分别为两字符串的长度[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值