給定兩個單詞 word1 和 word2,計算出將 word1 轉換成 word2 所使用的最少操作數 。
你可以對一個單詞進行如下三種操作:
插入一個字符
刪除一個字符
替換一個字符
示例 1:
輸入: word1 = "horse", word2 = "ros"
輸出: 3
解釋:
horse -> rorse (將 'h' 替換爲 'r')
rorse -> rose (刪除 'r')
rose -> ros (刪除 'e')
示例 2:
輸入: word1 = "intention", word2 = "execution"
輸出: 5
解釋:
intention -> inention (刪除 't')
inention -> enention (將 'i' 替換爲 'e')
enention -> exention (將 'n' 替換爲 'x')
exention -> exection (將 'n' 替換爲 'c')
exection -> execution (插入 'u')
依然按照步驟來:
1).定義一個能夠清楚描述最優子問題的數組(明確數組描述的含義)。
2).找出數組元素之間的關係式(狀態轉移方程)
3).找出初始值
1).dp[i][j] 代表第一個單詞的第i個字符變換到第二個單詞的第j個字符需要最少的步驟
2).狀態轉移方程 :
當兩個字符串中的某個字符相等時既 char1[i] == char2[j] dp[i][j] = dp[i-1][j-1]
否則,需要通過新增、修改、刪除等操作轉換 dp[i][j] = min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1 { PS:其中dp[i-1][j-1]表示dp[i][j] 由修改的狀態轉移過來 ,dp[i-1][j] 表示第一個單詞要刪除 ,dp[i][j-1]表示第一個單詞 新增}
3)初始化
for(int i = 0;i <= row;i++)
dp[i][0] = i;
for(int i = 0;i <= col;i++)
dp[0][i] = i;
code:
public static int minDistance(String word1, String word2) {
int row = word1.length();
int col = word2.length();
char[] char1 = word1.toCharArray();
char[] char2 = word2.toCharArray();
if (row == 0) {
return col;
}
if (col == 0) {
return row;
}
int dp[][] = new int[row + 2][col + 2]; //當字符串 word1 的長度爲 i,字符串 word2 的長度爲 j 時,將 word1 轉化爲 word2 所使用的最少操作次數爲 dp[i] [j]。
for(int i = 0;i <= row;i++)
dp[i][0] = i;
for(int i = 0;i <= col;i++)
dp[0][i] = i;
for(int i = 1;i <= row;i++) {
for(int j = 1;j <= col;j++) {
//字符串相同的話向下 無累加 移動
if(char1[i-1] == char2[j-1]) {
dp[i][j] = dp[i-1][j-1];
} else {
/**
* 修改、增加、刪除
*/
// 修改
int xg = dp[i - 1][j-1] ;
// 刪除
int sc = dp[i-1][j] ;
// 新增
int xz = dp[i][j-1] ;
dp[i][j] = Math.min(xg,Math.min(sc,xz)) + 1;
}
}
}
for(int i = 0;i <= row ;i++){
for(int j = 0;j <= col;j++){
System.out.print(dp[i][j] +" ");
}
System.out.println();
}
return dp[row][col];
}
優化
同我們之前做的DP類似,在求解的時候發現並不需要保存整個矩陣的狀態,只需要保存當前行對應的列即可。但是不同之處在於在當前位置dp[i][j]的左上角還有一個狀態,爲了同時保存左上角的狀態 解決的辦法是使用兩個變量temp
和pre
保存,在更新之前,使用temp
保存dp[j]
,然後dp[j]
要被更新,然後將dp[j]
賦值給pre
,下次遞推的時候,左上角的值就是pre
;
public int minDistance(String word1, String word2) {
char[] chs1 = word1.toCharArray();
char[] chs2 = word2.toCharArray();
int[] dp = new int[word2.length() + 1]; //注意要 + 1因爲一開始是空串
for (int j = 0; j < word2.length() + 1; j++) dp[j] = j;
char c1, c2;
int temp, pre;
for (int i = 1; i < word1.length() + 1; i++) {
pre = dp[0]; //上一排的
dp[0] = i; //這一排新dp[0]
for (int j = 1; j < word2.length() + 1; j++) {
c1 = word1.charAt(i - 1);
c2 = word2.charAt(j - 1); //注意下標對應的關係
temp = dp[j]; // 先要保存,因爲等下就更新了
dp[j] = c1 == c2 ? pre : min(dp[j], dp[j - 1], pre) + 1;
pre = temp; //記得先保存一下左上角的 pre的值(在二維的dp中就是dp[i-1][j-1])
}
}
return dp[word2.length()];
}
加強問題(ic,dc,rc)
題目:
給定兩個字符串str1和str2,再給定三個整數ic、dc和rc,分別代表插入、刪除和替換一個字符的代價,返回將str1編輯成str2的最小代價。
這個問題是上面問題的加強版,不同的地方在於這裏三個編輯的代價不同,所以我們要更加的清楚是哪個編輯的更新:
code:
class Solution {
/**
* 給定的 ic, dc ,rc 分別代表的是 插入,刪除 取代的代價
* 普通二維dp
*/
public int minDistance(String word1, String word2, int ic, int dc, int rc) {
char[] chs1 = word1.toCharArray();
char[] chs2 = word2.toCharArray();
int[][] dp = new int[chs1.length + 1][chs2.length + 1];
for (int i = 0; i < chs1.length + 1; i++) dp[i][0] = i * dc; //chs1是 , chs2是"" ->要刪除
for (int j = 0; j < chs2.length + 1; j++) dp[0][j] = j * ic; //chs1 是"" 轉換成chs2 -> 要添加
for (int i = 1; i < chs1.length + 1; i++) {
for (int j = 1; j < chs2.length + 1; j++) {
dp[i][j] = chs1[i - 1] == chs2[j - 1] ? dp[i - 1][j - 1]
: min(dp[i][j - 1] + ic, dp[i - 1][j] + dc, dp[i - 1][j - 1] + rc); //dp[i-1][j]代表的是刪除了 chs1[i-1] 所以加上dc
}
}
return dp[chs1.length][chs2.length];
}
public int min(int a, int b, int c) {
return Math.min(Math.min(a, b), c);
}
}
reference : https://blog.csdn.net/zxzxzx0119/article/details/82054807