给定两个字符串str1和str2,再给定三个整数ic,dc和rc,分别代表插入、删除和替换一个字符的代价,请输出将str1编辑成str2的最小代价。
数据范围:,
要求:空间复杂度 ,时间复杂度
class Solution { public: /** * min edit cost * @param str1 string字符串 the string * @param str2 string字符串 the string * @param ic int整型 insert cost * @param dc int整型 delete cost * @param rc int整型 replace cost * @return int整型 */ int minEditCost(string str1, string str2, int ic, int dc, int rc) { // write code here int N = str1.length(); int M = str2.length(); int dp[N][M]; dp[0][0] = str1[0] == str2[0] ? 0 : rc; for(int i = 1; i < N; i++){ if(str1[i] == str2[0]){ dp[i][0] = i * dc; } else{ dp[i][0] = min(dp[i-1][0] + dc, rc + i*dc); } } for(int j = 1; j < M; j++){ if(str1[0] == str2[j]){ dp[0][j] = j * ic; } else{ dp[0][j] = min(dp[0][j-1] + ic, rc + j*ic); } } for(int i = 1; i < N; i++){ for(int j = 1; j < M; j++){ dp[i][j] = min(dp[i-1][j] + dc, dp[i][j-1] + ic); if(str1[i] == str2[j]){ dp[i][j] = min(dp[i][j], dp[i-1][j-1]); } else{ dp[i][j] = min(dp[i][j], dp[i-1][j-1] + rc); } } } return dp[N-1][M-1]; } };
import java.util.*; public class Solution { /** * min edit cost * @param str1 string字符串 the string * @param str2 string字符串 the string * @param ic int整型 insert cost * @param dc int整型 delete cost * @param rc int整型 replace cost * @return int整型 */ public int minEditCost (String str1, String str2, int ic, int dc, int rc) { int m = str1.length(); int n = str2.length(); int[][] dp = new int[m + 1][n + 1]; // 行是str1,列是str2,题目要求把str1编辑成str2 for (int i = 1; i <= m; i++) dp[i][0] = dp[i - 1][0] + dc; // str1 位置 i 字符 变成 str2 空字符 —— 需要delete for (int j = 1; j <= n; j++) dp[0][j] = dp[0][j - 1] + ic; // str1 空字符 变成 str2 位置 i 字符 —— 需要insert for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (str1.charAt(i - 1) == str2.charAt(j - 1)) { // 字符相同,无需花费cost dp[i][j] = dp[i - 1][j - 1]; } else { // 在insert,delete 和 replace中找到 cost 最小的一个 dp[i][j] = Math.min(dp[i - 1][j - 1] + rc, Math.min(dp[i - 1][j] + dc, dp[i][j - 1] + ic)); } } } return dp[m][n]; } }
import java.util.*; public class Solution { public int minEditCost (String str1, String str2, int ic, int dc, int rc) { if(str1==null || str2==null) return 0; int m = str1.length(), n = str2.length(); int[][] dp = new int[m+1][n+1]; for(int i=0; i<=m; i++) dp[i][0] = i*dc; for(int i=0; i<=n; i++) dp[0][i] = i*ic; for(int i=1; i<=m; i++){ for(int j=1; j<=n; j++){ if(str1.charAt(i-1)==str2.charAt(j-1)){ dp[i][j] = dp[i-1][j-1]; }else{ int a = dp[i][j-1] + ic; int b = dp[i-1][j] + dc; int c = dp[i-1][j-1] + rc; dp[i][j] = Math.min(a, Math.min(b, c)); } } } return dp[m][n]; } }
class Solution { public: /** * min edit cost * @param str1 string字符串 the string * @param str2 string字符串 the string * @param ic int整型 insert cost * @param dc int整型 delete cost * @param rc int整型 replace cost * @return int整型 */ int minEditCost(string str1, string str2, int ic, int dc, int rc) { int len1=str1.size(),len2=str2.size(); vector<vector<int>> dp(len1+1,vector<int>(len2+1,0)); for(int i=1;i<=len2;i++) { dp[0][i]=ic*i; //只能增加 } for(int i=1;i<=len1;i++) { dp[i][0]=dc*i; //只能删除 } for(int i=1;i<=len1;i++) { for(int j=1;j<=len2;j++) { dp[i][j]=min(dp[i-1][j]+dc,dp[i][j-1]+ic); int t=dp[i-1][j-1]+((str1[i-1]==str2[j-1])?0:rc); dp[i][j]=min(t,dp[i][j]); // cout<<"("<<i<<","<<j<<"):"<<dp[i][j]<<endl; } } return dp[len1][len2]; } };
以 word1 为 "horse",word2 为 "ros",且 dp[5][3] 为例,即要将 word1的前 5 个字符转换为 word2的前 3 个字符,也就是将 horse 转换为 ros,因此有:
(1) dp[i-1][j-1],即先将 word1 的前 4 个字符 hors 转换为 word2 的前 2 个字符 ro,然后将第五个字符 word1[4](因为下标基数以 0 开始) 由 e 替换为 s(即替换为 word2 的第三个字符,word2[2])
(2) dp[i][j-1],即先将 word1 的前 5 个字符 horse 转换为 word2 的前 2 个字符 ro,然后在末尾补充一个 s,即插入操作
(3) dp[i-1][j],即先将 word1 的前 4 个字符 hors 转换为 word2 的前 3 个字符 ros,然后删除 word1 的第 5 个字符
class Solution { public: int minEditCost(string str1, string str2, int ic, int dc, int rc) { if(str1==""&&str2=="") return 0; int len1 = str1.size(); int len2 = str2.size(); //想清楚状态矩阵的定义,下标代表长度!! int dp[len1+1][len2+1]; for(int i=0;i<=len1;i++){ dp[i][0] = i*dc;//str1所有字符全部删除变成str2 } for(int j=0;j<=len2;j++){ dp[0][j] = j*ic;//空字符串str1全部插入变成str2 } 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{ //等价于str1的前i-1个字符和str2的前j-1个字符编辑距离的子问题。 //即对于str1的第i个字符'X',对于str2的第j个字符'W',我们在str1的末尾'X'替换为字符'W', //那么dp[i][j]最小可以为dp[i-1][j-1]+rc; int choice1 = dp[i-1][j-1] + rc;//替换 //等价于str1的前i个字符和str2的前j-1个字符编辑距离的子问题。 //即对于str2的第j个字符'X',我们在str1的末尾添加了一个相同的字符'X', //那么dp[i][j]最小可以为dp[i][j-1]+ic; int choice2 = dp[i][j-1]+ic;//插入 //等价于str1的前i-1个字符和str2的前j个字符编辑距离的子问题。 //即对于str1的第i个字符'X',我们在str2的末尾添加了一个相同的字符'X',等价于在str1的末尾删除了该字符'X', //那么dp[i][j]最小可以为dp[i][j-1]+dc; int choice3 = dp[i-1][j]+dc;//删除 dp[i][j] = min(choice1,min(choice2,choice3)); } } } return dp[len1][len2]; } };
/** * 牛客网:https://www.nowcoder.com/practice/05fed41805ae4394ab6607d0d745c8e4?tpId=191&tags=&title=&diffculty=0&judgeStatus=0&rp=1 * 每次操作都有对应的 cost * min edit cost * @param str1 string字符串 the string * @param str2 string字符串 the string * @param ic int整型 insert cost * @param dc int整型 delete cost * @param rc int整型 replace cost * @return int整型 * * 解法参考 leetcode 72. 编辑距离 * 本题比leetcode原题中增加了每种操作的代价。 * 原题的每种操作代价都是1 * dp[i][j] 表示 word1[0~i] 变成 word2[0~j] 需要的操作次数. * dp[i][j] = 1 + Math.min(dp[i-1][j], Math.min(dp[i][j-1], dp[i-1][j-1])); * 其中:已知 dp[i-1][j] 则 dp[i][j] 删除一个元素变成 dp[i-1][j] ,由 dp[i][j-1] 插入一个字符,由 dp[i-1][j-1] 替换一个元素 * */ public int minEditCost (String str1, String str2, int ic, int dc, int rc) { // 如果其中一个为空 if (str1.length() == 0) return str2.length() * ic; if (str2.length() == 0) return str1.length() * dc; int n1 = str1.length(), n2 = str2.length(); // dp[0][0] 表示空字符串变成空字符串的代价(0),可以简化操作 int[][] dp = new int[n1 + 1][n2 + 1]; // 初始化: // 1、由 str1 变成空字符串的代价 for (int i = 0; i <= n1; i++) dp[i][0] = i * dc; // 2、由空字符串变成 str2 的代价 for (int i = 0; i <= n2; i++) dp[0][i] = i * ic; // 状态转移 for (int i = 1; i <= n1; i++) { for (int j = 1; j <= n2; j++) { if (str1.charAt(i - 1) == str2.charAt(j - 1)) dp[i][j] = dp[i-1][j-1]; else dp[i][j] = Math.min(dp[i-1][j] + dc, Math.min(dp[i][j-1] + ic, dp[i-1][j-1] + rc)); } } return dp[n1][n2]; }
class Solution { public: /** * min edit cost * @param str1 string字符串 the string * @param str2 string字符串 the string * @param ic int整型 insert cost * @param dc int整型 delete cost * @param rc int整型 replace cost * @return int整型 */ int minEditCost(string a, string b, int ic, int dc, int rc) { int n = a.size(), m = b.size(); a = ' ' + a, b = ' ' + b; // f[i][j] : 将a[1 - i]变成b[1 - j]的的所有按顺序操作方案的最小值 vector<vector<int>> f(n + 1, vector<int>(m + 1)); for (int i = 1; i <= n; ++i) f[i][0] = i * dc; for (int i = 1; i <= m; ++i) f[0][i] = i * ic; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1]; else { int t1 = f[i - 1][j] + dc; int t2 = f[i][j - 1] + ic; int t3 = f[i - 1][j - 1] + rc; f[i][j] = min(t1, min(t2, t3)); } } } return f[n][m]; } };[LeetCode72:](https://leetcode-cn.com/problems/edit-distance/)
class Solution: def minEditCost(self , str1 , str2 , ic , dc , rc ): str1_length = len(str1) str2_length = len(str2) dp = [[0 for i in range(str2_length)] for j in range(str1_length)] # str1的前i个数转换为str2的前j个数的最小cost for j in range(str2_length): dp[0][j] = dp[0][j - 1] + ic for i in range(str1_length): dp[i][0] = dp[i - 1][0] + dc dp[0][0] = 0 if str1[0] == str2[0] else min(rc, dc + ic) # 如果第一个数相等,则是为0,反之,则是替换或删除+增加的最小值 for i in range(1, str1_length): for j in range(1, str2_length): cost = 0 if str1[i] == str2[j] else min(rc, dc + ic) dp[i][j] = min(dp[i-1][j-1] + cost, dp[i-1][j] + dc, dp[i][j-1] + ic) # 3种情况 return dp[str1_length-1][str2_length-1]
class Solution { public: int minEditCost(string str1, string str2, int ic, int dc, int rc) { int len1 = str1.length(); int len2 = str2.length(); vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0)); for (int i = 1; i <= len1; i++){ dp[i][0] = i * dc; } for (int j = 1; j <= len2; j++){ dp[0][j] = j * ic; } 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 dp[i][j] = min({dp[i][j - 1] + ic, dp[i - 1][j] + dc, dp[i -1][j - 1] + rc}); } return dp[len1][len2]; } };
public int minEditCost (String str1, String str2, int ic, int dc, int rc) { // write code here int l1=str1.length() ,l2=str2.length(); int[][] dp=new int[l1+1][l2+1]; for(int i=0;i<=l1;i++){ // 假设将str1编辑为str2,纵向为dc,横向为ic,对角为rc for(int j=0;j<=l2;j++){ if(i==0){ dp[i][j]=ic*j; }else if(j==0){ dp[i][j]=dc*i; }else{ if(str1.charAt(i-1)==str2.charAt(j-1)){ dp[i][j]=dp[i-1][j-1]; }else{ dp[i][j]=Math.min(Math.min(dp[i-1][j]+dc ,dp[i][j-1]+ic) ,dp[i-1][j-1]+rc); } } } } return dp[l1][l2]; }
public int minEditCost(String str1, String str2, int ic, int dc, int rc) { int m = str1.length(); int n = str2.length(); // dp[i][j] 表示的意思就是将 str1 的前 i 哥字符转为 str2 的前 j 个字符需要的代价 int[][] dp = new int[m + 1][n + 1]; // 初始化第一行和第一列 for (int i = 1; i <= m; i++) { dp[i][0] = i * dc; // 将 str1 的前 i 个字符删除,转为空字符串 } for (int j = 1; j <= n; j++) { dp[0][j] = j * ic; // 将空字符串转为 str2 的前 j 个字符,需要 j 次插入操作 } // 动态规划计算最小编辑代价 for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (str1.charAt(i - 1) == str2.charAt(j - 1)) { dp[i][j] = dp[i - 1][j - 1]; } else { int insert = dp[i][j - 1] + ic; int delete = dp[i - 1][j] + dc; int replace = dp[i - 1][j - 1] + rc; dp[i][j] = Math.min(insert, Math.min(delete, replace)); } } } return dp[m][n]; }
public int minEditCost (String str1, String str2, int ic, int dc, int rc) { // write code here int n1 = str1.length(); int n2 = str2.length(); int[][] dp = new int[n1 + 1][n2 + 1]; for (int i = 1; i <= n1; i++) dp[i][0] = dp[i - 1][0] + dc; for (int i = 1; i <= n2; i++) dp[0][i] = dp[0][i - 1] + ic; for (int i = 1; i <= n1; i++) { for (int j = 1; j <= n2; j++) { if (str1.charAt(i - 1) == str2.charAt(j - 1)) { dp[i][j] = dp[i - 1][j - 1]; } else { // dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1; dp[i][j] = Math.min(dp[i-1][j-1] + rc,Math.min(dp[i-1][j]+dc,dp[i][j-1]+ic)); } } } return dp[n1][n2]; }
public int minEditCost(String word1, String word2, int ic, int dc, int rc) { // 输出将str1编辑成str2的最小代价 int n1 = word1.length(); int n2 = word2.length(); int[][] dp = new int[n1 + 1][n2 + 1]; // 第一行:是 word1 为空,变成 word2 最少步数,就是插入操作 for (int j = 1; j <= n2; j++) { dp[0][j] = dp[0][j - 1] + ic; } // 第一列:是 word2 为空,需要的最少步数,就是删除操作 for (int i = 1; i <= n1; i++) { dp[i][0] = dp[i - 1][0] + dc; } for (int i = 1; i <= n1; i++) { for (int j = 1; j <= n2; 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]), dp[i - 1][j]) + 1; dp[i][j] = Math.min(dp[i][j - 1] + ic, dp[i - 1][j] + dc); int tmp = Math.min(dp[i - 1][j - 1] + rc, dp[i - 1][j - 1] + ic + dc); dp[i][j] = Math.min(dp[i][j], tmp); /* dp[i][j] = Math.min(Math.min(dp[i][j - 1] + ic, dp[i - 1][j] + dc), Math.min(dp[i - 1][j - 1] + rc, dp[i - 1][j - 1] + ic + dc));*/ } } } return dp[n1][n2]; }
/** * min edit cost * @param str1 string字符串 the string * @param str2 string字符串 the string * @param ic int整型 insert cost * @param dc int整型 delete cost * @param rc int整型 replace cost * @return int整型 */ int minEditCost(char* s, char* t, int ic, int dc, int rc ) { // write code here int m = strlen(s); int n = strlen(t); // if (m >= n + 2) // return false; int** mined = (int**)malloc(sizeof(int*) * (m + 1)); for (int i = 0; i <= m; i++) { mined[i] = (int*)calloc(n + 1, sizeof(int)); } for (int ii = 1; ii <= m; ii++) { mined[ii][0] = ii*dc; } for (int iii = 1; iii <= n; iii++) { mined[0][iii] = iii*ic; } for (int j = 1; j <= m; j++) { for (int k = 1; k <= n; k++) { if (s[j - 1] == t[k - 1]) { mined[j][k] = mined[j - 1][k - 1]; } else { if ((mined[j - 1][k - 1] + rc) < (mined[j][k - 1] + ic)) { if ((mined[j - 1][k - 1] + rc) < (mined[j - 1][k] + dc)) { mined[j][k] = (mined[j - 1][k - 1] + rc); } else { mined[j][k] = (mined[j - 1][k] + dc); } } else { if ((mined[j][k - 1] + ic) < (mined[j - 1][k] + dc)) { mined[j][k] = (mined[j ][k - 1] + ic); } else { mined[j][k] = (mined[j - 1][k] + dc); } } //mined[j][k]=((mined[j-1][k-1]+1)>(mined[j][k-1]+1):) } } } // if (mined[m][n] == 1) // return true; return mined[m][n]; }
class Solution: def minEditCost(self , str1: str, str2: str, ic: int, dc: int, rc: int) -> int: # write code here m = len(str1) n = len(str2) dp = [[0 for j in range(n + 1)] for i in range(m + 1)] for i in range(1, m + 1): dp[i][0] = dp[i - 1][0] + dc for j in range(1, n + 1): dp[0][j] = dp[0][j - 1] + ic for i in range(1, m + 1): for j in range(1, n + 1): if str1[i - 1] == str2[j - 1]: dp[i][j] = dp[i - 1][j - 1] else: dp[i][j] = min(dp[i - 1][j] + dc, dp[i][j - 1] + ic, dp[i - 1][j - 1] + rc) return dp[-1][-1]二维动态规划
go实现:
func minEditCost(str1 string, str2 string, ic int, dc int, rc int) int { n1, n2 := len(str1), len(str2) dp := make([][]int, n1+1) for i := 0; i <= n1; i++ { dp[i] = make([]int, n2+1) } for i := 1; i <= n1; i++ { dp[i][0] += dp[i-1][0] + dc } for j := 1; j <= n2; j++ { dp[0][j] += dp[0][j-1] + ic } for i := 1; i <= n1; i++ { for j := 1; j <= n2; j++ { if str1[i-1] == str2[j-1] { dp[i][j] = dp[i-1][j-1] continue } dp[i][j] = min(dp[i-1][j]+dc, dp[i][j-1]+ic) dp[i][j] = min(dp[i][j], dp[i-1][j-1]+rc) } } return dp[n1][n2] } func min(a, b int) int { if a > b { return b } return a }
# -*- coding: utf-8 -*- # # min edit cost # @param str1 string字符串 the string # @param str2 string字符串 the string # @param ic int整型 insert cost # @param dc int整型 delete cost # @param rc int整型 replace cost # @return int整型 # class Solution: """ 题目: https://www.nowcoder.com/practice/05fed41805ae4394ab6607d0d745c8e4?tpId=117&&tqId=37801&rp=1&ru=/ta/job-code-high&qru=/ta/job-code-high/question-ranking 算法: 设dp[i][j]表示将字符串str1的前i个字符编辑为字符串str2的前j个字符所需的最小代价 ic、dc、rc分别代表插入、删除和替换一个字符的代价,m, n分别为str1,str2的长度 初始化: dp[0][0] = 0 # 空字符串 + 空字符串 = 空字符串,无需编辑 状态转移方程: dp[i][0] = i * dc dp[0][j] = j * ic 若str1[i-1] == str2[j-1]: dp[i][j] = dp[i - 1][j - 1] 否则: dp[i][j] = min(dp[i][j-1] + ic, # 对str1插入一个字符,进行匹配,匹配后,str2长度-1 dp[i-1][j] + dc # 删除字符str1[i-1],进行匹配,str1长度-1 dp[i - 1][j - 1] + rc) # 替换str1[i - 1]为str2[j - 1]进行匹配,两者长度均-1 返回值: dp[m][n] 复杂度: 时间复杂度:O(mn) 空间复杂度:O(mn) """ def minEditCost(self, str1, str2, ic, dc, rc): m, n = len(str1), len(str2) dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(1, m + 1): # str2长度为0,只能删除 dp[i][0] = i * dc for j in range(1, n + 1): # str1长度为0,只能插入 dp[0][j] = j * ic for i in range(1, m + 1): for j in range(1, n + 1): if str1[i - 1] == str2[j - 1]: dp[i][j] = dp[i - 1][j - 1] else: dp[i][j] = min(dp[i][j - 1] + ic, dp[i - 1][j] + dc, dp[i - 1][j - 1] + rc) return dp[m][n] if __name__ == '__main__': s = Solution() str1, str2, ic, dc, rc = "abc", "adc", 5, 3, 2 # str1, str2, ic, dc, rc = "abc", "adc", 5, 3, 100 res = s.minEditCost(str1, str2, ic, dc, rc) print res