712. 兩個字串的最小ASCII刪除和
阿新 • • 發佈:2020-12-22
連結串列演算法題(程式設計師面試寶典)
解題思路主要來源於leetcode官方與《程式設計師面試寶典》。
712. 兩個字串的最小ASCII刪除和
給定兩個字串s1, s2,找到使兩個字串相等所需刪除字元的ASCII值的最小和。
示例 1:
輸入: s1 = “sea”, s2 = “eat”
輸出: 231
解釋: 在 “sea” 中刪除 “s” 並將 “s” 的值(115)加入總和。
在 “eat” 中刪除 “t” 並將 116 加入總和。
結束時,兩個字串相等,115 + 116 = 231 就是符合條件的最小和。
示例 2:
輸入: s1 = “delete”, s2 = “leet”
輸出: 403
解釋: 在 “delete” 中刪除 “dee” 字串變成 “let”,
將 100[d]+101[e]+101[e] 加入總和。在 “leet” 中刪除 “e” 將 101[e] 加入總和。
結束時,兩個字串都等於 “let”,結果即為 100+101+101+101 = 403 。
如果改為將兩個字串轉換為 “lee” 或 “eet”,我們會得到 433 或 417 的結果,比答案更大。
注意:
0 < s1.length, s2.length <= 1000。
所有字串中的字元ASCII值在[97, 122]之間。
解題方法
參考資料1:https://mp.weixin.qq.com/s/ZhPEchewfc03xWv9VP3msg
解題思路1
class Solution { //備忘錄 int[][] memo; public int minimumDeleteSum(String s1, String s2) { //帶備忘錄的遞迴法 //與最長公共子序列演算法相似 memo = new int[s1.length()][s2.length()]; for(int[] row:memo){ //-1 表示未進行操作 Arrays.fill(row,-1); } return dp(s1,0,s2,0); } //定義:將 s1[i..] s2[j..] 刪除成相同的子序列,最小的 ASCII 碼之和為 dp(s1, i, s2, j) public int dp(String s1,int i,String s2,int j){ //base case int res = 0; if(i==s1.length()){ //如果一個字串為空,肯定要將另一個字串刪除完 for(;j<s2.length();j++){ res += s2.charAt(j); } return res; } if(j==s2.length()){ //如果一個字串為空,肯定要將另一個字串刪除完 for(;i<s1.length();i++){ res += s1.charAt(i); } return res; } if(memo[i][j]!=-1){ return memo[i][j]; } if(s1.charAt(i)==s2.charAt(j)){ //不進行刪除操作 memo[i][j] = dp(s1,i+1,s2,j+1); }else{ //s1[i] s2[j] 至少刪除一個 memo[i][j] = Math.min( s1.charAt(i) + dp(s1,i+1,s2,j), s2.charAt(j) + dp(s1,i,s2,j+1) ); } return memo[i][j]; } }
解題思路2
動態規劃,迭代
class Solution {
public int minimumDeleteSum(String s1, String s2) {
//動態規劃 迭代
//dp[i][j] 代表s1[1..i] s2[1..j] 刪除成相同子序列,最小的ASCII值
int[][] dp = new int[s1.length()+1][s2.length()+1];
//base case
for(int[] row:dp){
Arrays.fill(row,0);
}
int val = 0;
for(int i=1;i<=s1.length();i++){
val += s1.charAt(i-1);
dp[i][0] = val;
}
val = 0;
for(int i=1;i<=s2.length();i++){
val += s2.charAt(i-1);
dp[0][i] = val;
}
for(int i=1;i<=s1.length();i++){
for(int j=1;j<=s2.length();j++){
if(s1.charAt(i-1)==s2.charAt(j-1)){
//相同的字元不用進行刪除操作
dp[i][j] = dp[i-1][j-1];
}else{
//不同的話,s1[i-1] s2[j-1] 兩個至少刪除一個字元
dp[i][j] = Math.min(s1.charAt(i-1)+dp[i-1][j],s2.charAt(j-1)+dp[i][j-1]);
}
}
}
return dp[s1.length()][s2.length()];
}
}
解題思路3
class Solution {
public int minimumDeleteSum(String s1, String s2) {
//將最小ASCII刪除和 轉換為最大公共子序列的最大ASCII值和
//動態規劃 迭代
//dp[i][j] 代表s1[1..i] s2[1..j] 中最大公共子序列的最大ACSII值
int[][] dp = new int[s1.length()+1][s2.length()+1];
//base case
for(int[] row:dp){
Arrays.fill(row,0);
}
for(int i=1;i<=s1.length();i++){
for(int j=1;j<=s2.length();j++){
if(s1.charAt(i-1)==s2.charAt(j-1)){
//相同的字元
dp[i][j] = dp[i-1][j-1]+s1.charAt(i-1);
}else{
//不同的話,
dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
int sum = 0;
for(int i=0;i<s1.length();i++){
sum += s1.charAt(i);
}
for(int i=0;i<s2.length();i++){
sum += s2.charAt(i);
}
//返回值:兩數的ASCII值和 減去 2* 最大公共子序列的最大ASCII值和
return sum-2*dp[s1.length()][s2.length()];
}
}