dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。
当s[i - 1] 与 t[j - 1]相等时,dp[i][j]可以有两部分组成。
一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。即不需要考虑当前s子串和t子串的最后一位字母,所以只需要 dp[i-1][j-1]。
一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]。
例如: s:bagg 和 t:bag ,s[3] 和 t[2]是相同的,但是字符串s也可以不用s[3]来匹配,即用s[0]s[1]s[2]组成的bag。
当然也可以用s[3]来匹配,即:s[0]s[1]s[3]组成的bag。
所以当s[i - 1] 与 t[j - 1]相等时,dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
当s[i - 1] 与 t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配(就是模拟在s中删除这个元素),即:dp[i - 1][j]
所以递推公式为:dp[i][j] = dp[i - 1][j];
AC代码:
class Solution {
public:
int numDistinct(string s, string t) {
int n = s.size();
int m = t.size();
if (n < m) {
return 0;
}
// 0-i的s的子序列中0-j的t出现的个数
vector<vector<long long>> dp(n + 1, vector<long long>(m + 1));
// 初始化第一列
for (int i = 1; i <= n; i++) {
if (s[i - 1] == t[0]) {
dp[i][1] = dp[i - 1][1] + 1;
} else {
dp[i][1] = dp[i - 1][1];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 2; j <= m; ++j) {
if (s[i - 1] == t[j - 1]) {
//题目可能会超范围,不过题目保证答案在int范围内
//因此可以用INT_MAX取余来避免溢出
dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % INT_MAX;
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[n][m];
}
};
dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。
注意初始化,dp[i][0]不是全部等于1,而是就等于当前长度,因为j=0代表word2为空串,要变为空串,那么就是全部删除
class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.size();
int m = word2.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1));
for (int i = 1; i <= n; i++) {
dp[i][0] = dp[i - 1][0] + 1;
}
for (int j = 1; j <= m; ++j) {
dp[0][j] = dp[0][j - 1] + 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; ++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[n][m];
}
};
和583. 两个字符串的删除操作类似,不过多了一个替换操作,于是dp[i][j]就可由两个方向得到:删除(或者说插入,类似的效果)word1[i - 1] 或者 word2[j - 1]字符,即dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1;;或是替换当前字符,将word1[i - 1] 替换为 word2[j - 1],或者反过来,即dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1);,取这两种中的最小值
class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.size();
int m = word2.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1));
for (int i = 1; i <= n; i++) {
dp[i][0] = dp[i - 1][0] + 1;
}
for (int j = 1; j <= m; ++j) {
dp[0][j] = dp[0][j - 1] + 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; ++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;
// 替换
dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
}
return dp[n][m];
}
};