動態規劃求解子序列問題 和 編輯距離
阿新 • • 發佈:2022-04-12
動態規劃求解子序列問題
思路
這類題基本就三步:
- 確定動態陣列含義
- 寫出轉移方程
- 給出basecase(基礎解)
LeetCode 1143
- 確定動態陣列含義:dp[i] [j]為text1[0...i-1] 和 text2[0...j-1] 的 lcs(最長公共子序列) 長度
- 轉移方程:
- 當text1(i-1)==text2(j-1)時,dp[i] [j]=dp[i-1] [j-1] (注意字串的下標從零開始,dp陣列的i和j就是指字串的i-1和j-1)
- 當text1(i)!=text2(j)時,有三種情況:
- text1[0...i-1] 和 text2[0...j-1] 的lcs長度等於text1[0...i-2] 和 text2[0...j-1]的lcs長度
- text1[0...i-1] 和 text2[0...j-1] 的lcs長度等於text1[0...i-1] 和 text2[0...j-2]的lcs長度
- text1[0...i-1] 和 text2[0...j-1] 的lcs長度等於text1[0...i-2] 和 text2[0...j-2]的lcs長度(這種情況下的lcs長度一定不大於前兩種,可以直接忽略)
- 取三種情況的最大值即可
- 確定basecase
- 當其中一個字串為零時,lcs長度=0
題解:
class Solution { public int longestCommonSubsequence(String text1, String text2) { int m=text1.length(),n=text2.length(); if(m==0|| n==0) return 0; int[][] dp=new int[m+1][n+1]; // 定義:text1[0..i-1] 和 text2[0..j-1] 的 lcs 長度為 dp[i][j] // base case: dp[0][..] = dp[..][0] = 0 for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if(text1.charAt(i-1)==text2.charAt(j-1)) dp[i][j]=dp[i-1][j-1]+1; else { dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]); } } } return dp[m][n]; } }
LeetCode 583
- 確定動態陣列含義:dp[i] [j]為使得word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步數
- 轉移方程:
- 當word1(i-1)==word2(j-1)時,dp[i] [j]=dp[i-1] [j-1] (注意字串的下標從零開始,dp陣列的i和j就是指字串的i-1和j-1)
- 當word1(i)!=word2(j)時,有三種情況:
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步數等於word1[0...i-2] 和 text2[0...j-1]相同所需的最小步數+1(即刪除了word1[i-1])
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步數等於word1[0...i-1] 和 text2[0...j-2]相同所需的最小步數+1(即刪除了word2[j-1])
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步數等於word1[0...i-2] 和 text2[0...j-2]相同所需的最小步數+1(即刪除了word1[i-1]和word2[j-1])
- 取三種情況的最小值即可
- 確定basecase
- 當其中一個字串為零時,相同所需的最小步數為另一個字串的長度
題解:
class Solution {
public int minDistance(String word1, String word2) {
int m=word1.length(),n=word2.length();
int[][] dp=new int[m+1][n+1];
for (int i = 1; i <= m; i++) {
dp[i][0] = i;
}
for (int j = 1; j <=n ; j++) {
dp[0][j] = j;
}
for (int i = 1; i <=m ; i++) {
for (int j = 1; j <=n ; 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]+1), dp[i-1][j-1]+2);
}
}
}
return dp[m][n];
}
}
另一種思路,用上一題(1143)的結論,因為要使步數最小,也就是刪除兩個字串除了最長公共子序列之外的所有元素即可。程式碼如下:
class Solution {
public int minDistance(String word1, String word2) {
int m=word1.length(),n=word2.length();
int lcs = longestCommonSubsequence(word1,word2,m,n);
return m+n-2*lcs;
}
public int longestCommonSubsequence(String word1, String word2,int m,int n) {
if(m==0|| n==0) return 0;
int[][] dp=new int[m+1][n+1];
// 定義:s1[0..i-1] 和 s2[0..j-1] 的 lcs 長度為 dp[i][j]
// base case: dp[0][..] = dp[..][0] = 0
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if(word1.charAt(i-1)==word2.charAt(j-1)) dp[i][j]=dp[i-1][j-1]+1;
else {
dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[m][n];
}
}
LeetCode712
這題與上一題相似,直接給程式碼
class Solution {
public int minimumDeleteSum(String s1, String s2) {
int m=s1.length(),n=s2.length();
int[][] dp=new int[m+1][n+1];
for (int i = 1; i <= m; i++) {
dp[i][0] = dp[i-1][0]+s1.charAt(i-1);
}
for (int j = 1; j <=n ; j++) {
dp[0][j] = dp[0][j-1] + s2.charAt(j-1);
}
for (int i = 1; i <=m ; i++) {
for (int j = 1; j <=n ; j++) {
if(s1.charAt(i-1)==s2.charAt(j-1)) dp[i][j]=dp[i-1][j-1];
else {
dp[i][j]=Math.min(Math.min(dp[i-1][j]+s1.charAt(i-1),dp[i][j-1]+s2.charAt(j-1)), dp[i-1][j-1]+s1.charAt(i-1)+s2.charAt(j-1));
}
}
}
return dp[m][n];
}
}
動態規劃 編輯距離 LeetCode72
- 確定動態陣列含義:dp[i] [j]為word1[0...i-1] 和 word2[0...j-1] 的編輯距離
- 轉移方程:
- 當word1(i-1)==word2(j-1)時,dp[i] [j]=dp[i-1] [j-1] (注意字串的下標從零開始,dp陣列的i和j就是指字串的i-1和j-1)
- 當word1(i)!=word2(j)時,有三種情況:
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步數等於word1[0...i-2] 和 text2[0...j-1]相同所需的最小步數+1(即刪除了word1[i-1])
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步數等於word1[0...i-1] 和 text2[0...j-2]相同所需的最小步數+1(即刪除了word2[j-1])
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步數等於word1[0...i-2] 和 text2[0...j-2]相同所需的最小步數+1(即替換了word1[i-1]和word2[j-1]中的一個)
- 取三種情況的最小值即可
- 刪除一個字元等價於為另一個字串插入一個字元,顧都討論刪除
- 確定basecase
- 當其中一個字串為零時,相同所需的最小步數為另一個字串的長度
題解
class Solution {
public int minDistance(String word1, String word2) {
int m=word1.length(),n=word2.length();
int[][] dp =new int[m+1][n+1];
//s1[0..i-1] 和 s2[0..j-1] 的編輯距離表示為dp[i][j]
for (int i = 0; i <= m; i++) {
dp[i][0] = i;
}
for (int i = 0; i <= n; i++) {
dp[0][i]= i;
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; 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-1][j]), dp[i][j-1])+1;
}
}
return dp[m][n];
}
}