1055 字
5 分钟
Day45-动态规划 part12
115. 不同的子序列
1. 确定dp数组以及下标的含义
定义一个二维数组
dp[i][j],表示s的前i个字符中,组成t的前j个字符的子序列个数。
2. 确定递推公式
- 如果
s[i - 1] == t[j - 1],那么可以有两种选择:- 要用
s[i-1]来匹配t[j-1]:即dp[i-1][j-1] - 不用
s[i-1],直接继承前面的状态:即dp[i-1][j]
- 要用
- 因此递推公式为:
if (s[i - 1] == t[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j];
}3. 初始化
dp[0][0] = 1,空字符串可以匹配空字符串。dp[i][0] = 1,任何字符串都可以通过删除全部字符得到空字符串。dp[0][j] = 0(j > 0),空字符串无法组成非空字符串。
vector<vector<long long>> dp(s.size() + 1, vector<long long>(t.size() + 1, 0));
dp[0][0] = 1;
for (int i = 1; i <= s.size(); i++) dp[i][0] = 1;4. 遍历顺序
从 i = 1 遍历到 s.size(),j = 1 遍历到 t.size(),先遍历 s,再遍历 t。
for (int i = 1; i <= s.size(); i++) {
for (int j = 1; j <= t.size(); j++) {
...
}
}5. 整体代码
class Solution {
public:
int numDistinct(string s, string t) {
vector<vector<long long>> dp(s.size() + 1, vector<long long>(t.size() + 1, 0));
for (int i = 0; i <= s.size(); i++) dp[i][0] = 1;
for (int i = 1; i <= s.size(); i++) {
for (int j = 1; j <= t.size(); j++) {
if (s[i - 1] == t[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[s.size()][t.size()];
}
};583. 两个字符串的删除操作
1. 确定dp数组以及下标的含义
定义
dp[i][j]表示将word1的前i个字符和word2的前j个字符变得相同所需要删除的最少步数。
2. 确定递推公式
- 如果
word1[i-1] == word2[j-1],那么不需要删除字符,继承前面的状态:dp[i][j] = dp[i-1][j-1]; - 如果
word1[i-1] != word2[j-1],要么删word1[i-1],要么删word2[j-1],取最小值再加一:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1;
3. 初始化
dp[i][0] = i,将word1删除成空字符串dp[0][j] = j,将word2删除成空字符串
vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;4. 遍历顺序
从 i = 1 遍历到 word1.size(),j = 1 遍历到 word2.size(),从前向后遍历。
for (int i = 1; i <= word1.size(); i++) {
for (int j = 1; j <= word2.size(); j++) {
...
}
}5. 整体代码
class Solution {
public:
int minDistance(string word1, string word2) {
vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
for (int i = 1; i <= word1.size(); i++) {
for (int j = 1; j <= word2.size(); 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], dp[i][j - 1]) + 1;
}
}
}
return dp[word1.size()][word2.size()];
}
};72. 编辑距离
1. 确定dp数组以及下标的含义
定义
dp[i][j]表示将word1的前i个字符转换成word2的前j个字符所使用的最少编辑操作次数。
编辑操作包括:插入、删除、替换。
2. 确定递推公式
- 如果
word1[i-1] == word2[j-1],则不需要任何操作:dp[i][j] = dp[i-1][j-1]; - 如果
word1[i-1] != word2[j-1],则考虑三种情况:- 插入:
dp[i][j-1] + 1 - 删除:
dp[i-1][j] + 1 - 替换:
dp[i-1][j-1] + 1
- 插入:
取三者最小值:
dp[i][j] = min({dp[i-1][j] + 1, dp[i][j-1] + 1, dp[i-1][j-1] + 1});3. 初始化
dp[i][0] = i,把word1变成空字符串dp[0][j] = j,把空字符串变成word2
vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;4. 遍历顺序
从 i = 1 遍历到 word1.size(),j = 1 遍历到 word2.size(),先遍历 word1 再遍历 word2。
for (int i = 1; i <= word1.size(); i++) {
for (int j = 1; j <= word2.size(); j++) {
...
}
}5. 整体代码
class Solution {
public:
int minDistance(string word1, string word2) {
vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
for (int i = 1; i <= word1.size(); i++) {
for (int j = 1; j <= word2.size(); 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, dp[i][j - 1] + 1, dp[i - 1][j - 1] + 1});
}
}
}
return dp[word1.size()][word2.size()];
}
};
