leetcode-72 编辑距离
DP双序列典型题,这种题往往都是2维dp,两个维度分别代表两个序列上的位置。这道题的思路就是设dp[i][j]为word1前i个和word2前j个的编辑距离,如果当前两个字符串的字符一致则跳过,否则改增删分别对应一种状态转移,取三者最小值。改对应dp[i-1][j-1]+1,删(删word1)对应dp[i-1][j]+1,增(也是增word1)对应dp[i][j-1]+1。这里增稍微有点不好想,可以理解为增word1等效于删word2。需要注意的细节是,将dp数组开成m+1,n+1是为了便于处理base case,否则在base case就要处理改增删逻辑,很麻烦。
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size(), n = word2.size();
if(m == 0) return n;
if(n == 0) return m;
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
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[i - 1] == word2[j - 1]) dp[i][j] = dp[i - 1][j - 1];
else{
dp[i][j] = min({dp[i - 1][j - 1] + 1, dp[i - 1][j] + 1, dp[i][j - 1] + 1});
}
}
}
return dp[m][n];
}
};
leetcode-1143 最长公共子序列
这题和编辑距离很像,同属双序列的DP题。定义dp[i][j]为text1到位置i,text2到位置j为止的最长公共子序列长度。注意这里定义其实不是很好,应该参考上面定义为前i个,从下面代码里能看出这样定义的base case处理很麻烦。状态转移也跟编辑距离很像,当前两个字母相等则dp[i][j] = dp[i - 1][j - 1] + 1,否则dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])。
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m = text1.size(), n = text2.size();
vector<vector<int>> dp(m, vector<int>(n));
bool flag = false;
for(int i = 0; i < m; ++i){
if(flag){
dp[i][0] = 1;
continue;
}
if(text1[i] == text2[0]){
flag = true;
dp[i][0] = 1;
}
}
flag = false;
for(int j = 0; j < n; ++j){
if(flag){
dp[0][j] = 1;
continue;
}
if(text2[j] == text1[0]){
flag = true;
dp[0][j] = 1;
}
}
for(int i = 1; i < m; ++i){
for(int j = 1; j < n; ++j){
if(text1[i] == text2[j]){
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[m - 1][n - 1];
}
};
后面还做了不同路径1、2,最小路径和。这种类型题基本熟了就不记了。需要注意这种题用DP解的前提是不能绕回来,也就是不能出现循环依赖。如果能绕回来则需要用最短路之类的。