您的位置:首页 > 其它

左神算法 最小编辑代价

2018-01-29 17:16 232 查看
【题目】

给定两个字符串str1和str2,在给定三个整数ic,dc和rc,分别代表插入、删除和替换一个字符的代价,返回将str1编辑成str2的最小代价

【举例】str1 = “abc”

str2 = “adc”,ic = 5, dc=3,rc=2

【1】从abc编辑成adc,把b替换成d代价是最小的,返回2

【2】如果rc =100,先删除b,再插入d代价是最小的,返回8

【3】如果str1 == str2 说明不用修改,返回0

用动态规划解题

m = str1.length(), n = str2.length()

申请dp,行数是m+1,列数是n+1。这里我规定str1是行,str2是列。

dp[i][j]代表str1的下标是0….i-1个字符 和 str2的下标是0….j-1字符的最小编辑代价。

如果申请的行列数还是字符串的长度,不加一行做空字符串匹配。那么在还是按之前的动态规划方法做的时候会发现问题,第一行和第一列很难确定它的值。因为有多种选择,第一行有一个str1中的第一个字符了,这时候是选择替换,删除,还是增加呢。状态不好确定。

所以第一行从空字符开始,对于dp[0][j]表示str1还是空串的时候,它去匹配str2的str2[0,1….j-1]字符串,很明显,只有添加的情况,dp[0][j]就是一个等差数列。

同理,第一列就是只有删除的情况。

从dp[1][1]开始填表,dp[i][j]的状态取决于斜上格,左格,和上格。

【犯错】这里我犯了一个错误。

在计算dp[i][j]的时候,在状态转移中如果str1[i-1]和str2[j-1]的字符相等,dp[i][j] = dp[i-1][j-1],这里我认为只有这种情况了。其实是错误的,还是要和左格和上格比较,取最小值。在这题中,我不能保证dp[i-1][j-1]<= dp[i-1][j]和dp[i-1][j-1]<=dp[i][j-1],这两个公式是不能保证成立的。因为dp[i][j-1]也可能从它的斜上方迁跃过来。从图中也可以看到这个公式是不成立的,请看第5行第6列的值4,小于它的左边5。

所以如果相等,还是要继续做比较,我下面的代码就是有问题的。

【问题代码】

public class Main {
public static void main(String[] args) {
String str1 = "ab12cd3";
String str2 = "abcdf";
int ic = 5, dc = 3, rc =2;
System.out.println(minCost(str1, str2, ic, dc, rc));
}

public static int minCost(String str1, String str2, int ic, int dc, int rc){
if (str1 == null ||str2 == null || str1.length() == 0 ||str2.length() == 0){
return 0;
}
int[][] dp = new int[str1.length() + 1][str2.length() + 1];
for (int i = 1; i <= str2.length(); i++) {
dp[0][i] = dp[0][i-1] + ic;
}
for (int i = 1; i <= str1.length() ; i++) {
dp[i][0] = dp[i-1][0] + dc;
}
for (int i = 1; i <=str1.length() ; i++) {
for (int j = 1; j <= str2.length(); 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] + rc, Math.min(dp[i][j-1] + ic, dp[i-1][j] + dc));
}
}
}
return dp[str1.length()][str2.length()];
}
}


【状态转移】

这里再说明下状态转移,就有三种情况,dp[i][j]:

【1】dp[i][j-1] + ic ,这是str1的当前子串和str2当前子串的最后一个字符之前的子串调整后匹配了,又多了一个str2的字符,为了匹配上,得插入,代价是ic。

【2】dp[i-1][j] + dc ,str1的当前子串的最后一个字符之前的子串和str2当前子串匹配了, 这是str1又多了一个字符,为了匹配上,得删除,代价是dc

【2】dp[i-1][j-1] + rc,str1的当前子串的最后一个字符之前的子串和str2当前子串的最后一个字符之前的子串匹配了,如果str1当前子串的最后一个字符和str2当前子串的最后一个字符不相等,就得加上rc。如果相等,就是dp[i-1][j-1]

【正确代码如下】、

public class Main {
public static void main(String[] args) {
String str1 = "ab12cd3";
String str2 = "abcdf";
int ic = 5, dc = 3, rc =2;
System.out.println(minCost(str1, str2, ic, dc, rc));
}

public static int minCost(String str1, String str2, int ic, int dc, int rc){
if (str1 == null ||str2 == null || str1.length() == 0 ||str2.length() == 0){
return 0;
}
int[][] dp = new int[str1.length() + 1][str2.length() + 1];
for (int i = 1; i <= str2.length(); i++) {
dp[0][i] = dp[0][i-1] + ic;
}
for (int i = 1; i <= str1.length() ; i++) {
dp[i][0] = dp[i-1][0] + dc;
}
for (int i = 1; i <=str1.length() ; i++) {
for (int j = 1; j <
4000
= str2.length(); j++) {
if (str1.charAt(i-1) == str2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1];
}
else {
dp[i][j] = dp[i-1][j-1] +rc;   //修改之处
}
dp[i][j] = Math.min(dp[i][j], Math.min(dp[i][j-1] + ic, dp[i-1][j] + dc));  //修改之处

}
}
return dp[str1.length()][str2.length()];
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: