Description
Given two strings s1, s2, find the lowest ASCII sum of deleted characters to make two strings equal.
Example 1:
Input: s1 = “sea”, s2 = “eat”
Output: 231
Explanation: Deleting “s” from “sea” adds the ASCII value of “s” (115) to the sum. Deleting “t” from “eat” adds 116 to the sum. At the end, both strings are equal, and 115 + 116 = 231 is the minimum sum possible to achieve this.
Note:
0 < s1.length, s2.length <= 1000.
All elements of each string will have an ASCII value in [97, 122].
Analyze
虽然跟最短编辑距离很像,但明眼人应该一眼就看出来,这就是个最长子序列的变形题。求完最长子序列,然后用总ASCII和减掉两倍的最大子序列和即可。
动态规划的状态转移方程如下:
有了状态转移方程写代码思路很清晰的,so easy一遍过。
Code
int minimumDeleteSum(string s1, string s2) {
int m = s1.size();
int n = s2.size();
int** arr = new int*[m];
for(int i = 0; i < m; ++i)
arr[i] = new int[n];
for(int i = 0; i < m; ++i) {
if(s2[0] == s1[i] || (i && arr[i-1][0]))
arr[i][0] = s2[0];
else arr[i][0] = 0;
}
for(int j = 0; j < n; ++j) {
if(s1[0] == s2[j] || (j && arr[0][j-1]))
arr[0][j] = s1[0];
else arr[0][j] = 0;
}
for(int i = 1; i < m; ++i) {
for(int j = 1; j < n; ++j) {
if(s1[i] == s2[j])
arr[i][j] = arr[i-1][j-1] + s1[i];
else if(arr[i-1][j] >= arr[i][j-1])
arr[i][j] = arr[i-1][j];
else arr[i][j] = arr[i][j-1];
}
}
int sum = 0;
for(string::iterator it = s1.begin(); it != s1.end(); ++it)
sum += *it;
for(string::iterator it = s2.begin(); it != s2.end(); ++it)
sum += *it;
return sum - 2*arr[m-1][n-1];
}
Summary
又瞻仰了一下人家十行的代码,学习到的点如下:
1.我绕了个圈子
我先求了最大公共子序列再去减,殊不知转变一下思路可以有更直接的解法
//dp[i][j] is the cost for s1.substr(0,i) and s2.substr(0, j).
//Note s1[i], s2[j] not included in the substring.
//Base case: dp[0][0] = 0
//target: dp[m][n]
if s1[i-1] = s2[j-1] // no deletion
dp[i][j] = dp[i-1][j-1];
else // delete either s1[i-1] or s2[j-1]
dp[i][j] = min(dp[i-1][j]+s1[i-1], dp[i][j-1]+s2[j-1]);
2.简化前面的步骤
漂亮的二维数组申请方法:
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
i=0或者j=0的时候i-1这样的下标肯定是越界的,因此我需要额外用两个循环处理初始状态。而大佬申请数组时就多了一行一列,并初始化为全0。多出来的行用一个循环初始化为s2(0,j)的ASCII和,列的处理类似不过做了更加简化的处理,而省掉了一个循环。
不得不说人家的状态转移方程就比我们考虑地更加简洁优雅,能照顾到初始状况。