目录
算法运用场景:
字符串之间最小距离是非常经典的题目,运用也非常广泛
比如在搜索中,输入:“数组的输出格式” 结果中:“字符数组输出格式”,“数组的输入输出”,
结果与我们搜索的内容虽然不一样,但是字符串距离却很小(相似)
题目:
给定两个字符串str1和str2,再给定三个整数ic,dc和rc,分别代表插入、删除和替换一个字符的代价,请输出将str1编辑成str2的最小代价。
输入描述:
输出三行,第一行和第二行均为一行字符串,分别表示两个字符串str1,str2。。第三行为三个正整数,代表ic,dc和rc。(1<=ic<=10000、1<=dc<=10000、1<=rc<=10000)
输出描述:
输出一个整数,表示编辑的最小代价。
示例1
输入
abc adc 5 3 2
输出
2
示例2
输入
abc adc 5 3 100
输出
8
示例3
输入
abc abc 5 3 2
输出
0
备注:
时间复杂度O(N*M),空间复杂度O(N)。(n,m代表两个字符串长度)
思路分析:
- 创建一个dp数组,dp[i][j]表示str1[0...i-1]组成的子串与str2[0...j-1]组成的子串的最优解
- 即长度为i的str1转换成长度为j的str2所需的最小代价
- 初始化:
- 首行:str1长度为0即空串。所以str1转换成str2必须用insert,即dp[0][j] = ic * j;
- 首列:str2长度为0即空串。所以str1转换成str2必须用delete,即dp[i][0] = dc * j;
- 其余点:
- str1[0...i-2]转换成str2[0...j-2]的代价 加上 将str1[i-1]转换成str[j-1]
- 若str1[i-1] = str2[j-1] 则dp[i-1][j-1] + rc*0;(最后一个元素相等不需要replace)
- 若str1[i-1] != str[j-1] 则dp[i-1][j-1] +rc*1;
- str1[0...i-2]转换成str2[0...j-1]的代价 加上 删去str1[i-1]的代价
- 即dp[i-1][j] +dc;
- str[0...i-1]的转换成str2[0...j-2]的代价 加上 在str1的末尾添加str2[j-1]的代价
- 即dp[i][j-1] +ic;
- 在以上三种可能性中取最小值
- str1[0...i-2]转换成str2[0...j-2]的代价 加上 将str1[i-1]转换成str[j-1]
- 结果:
- dp[n][m]即为结果
优化:
题目要求空间复杂度为O(N) ,考虑用空间压缩的技巧
每个dp都只依赖与 左边,上面,与左上。所以用一个一维数组不断更新即可以实现
样例推导:
二维矩阵:
空间压缩:
代码展示:
二维矩阵:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class Main{
private static boolean DEBUG = false;
public static void main(String[] args)throws IOException{
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String str1 = reader.readLine();
String str2 = reader.readLine();
String[] idr = reader.readLine().split(" ");
int ic = Integer.parseInt(idr[0]);
int dc = Integer.parseInt(idr[1]);
int rc = Integer.parseInt(idr[2]);
int n = str1.length();
int m = str2.length();
int dp[][] = new int[n+1][m+1];
//初始化
for(int j=0;j<=m;j++){//首行
dp[0][j] = j * ic;
}
for(int i=0;i<=n;i++){
dp[i][0] = i * dc;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int p1 = dp[i-1][j-1] + rc * (str1.charAt(i-1)==str2.charAt(j-1)?0:1);
int p2 = dp[i-1][j] + dc;
int p3 = dp[i][j-1] + ic;
dp[i][j] = Math.min(p1,Math.min(p2,p3));
if(DEBUG) System.out.print(dp[i][j] + " ");
}
if(DEBUG) System.out.println();
}
System.out.print(dp[n][m]);
}
}
空间压缩:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class Main{
private static boolean DEBUG = false;
public static void main(String[] args)throws IOException{
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String str1 = reader.readLine();
String str2 = reader.readLine();
String[] idr = reader.readLine().split(" ");
int ic = Integer.parseInt(idr[0]);
int dc = Integer.parseInt(idr[1]);
int rc = Integer.parseInt(idr[2]);
int n = str1.length();
int m = str2.length();
int dp[] = new int[m+1];
//初始化
for(int j=0;j<=m;j++){//首行
dp[j] = j * ic;
}
int temp = 0;
for(int i=1;i<=n;i++){
temp = dp[0];
dp[0] = i * dc;
if(DEBUG) System.out.print(dp[0] + " ");
for(int j=1;j<=m;j++){
int p1 = temp + rc * (str1.charAt(i-1)==str2.charAt(j-1)?0:1);
temp = dp[j];
int p2 = dp[j] + dc;
int p3 = dp[j-1] + ic;
dp[j] = Math.min(p1,Math.min(p2,p3));
if(DEBUG) System.out.print(dp[j] + " ");
}
if(DEBUG) System.out.println();
}
System.out.print(dp[m]);
}
}