代码随想录算法训练营第五十一天 | 115.不同的子序列 583.两个字符串的删除操作 72.编辑距离
115.不同的子序列
动规五部曲:
- dp[i][j]: 以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j],
- 递推公式:
- s[i-1]与t[j-1]相等
- 需要是s[i-1]与t[j-1]匹配,那么个数为dp[i-1][j-1]即不需要考虑当前s子串和t子串的最后一位字母,所以只需要dp[i-1][j-1]
- 不用s[i-1]来匹配,个数为dp[i-1][j]
- s[i-1]与t[j-1]不相等: dp[i-1][j]
- s[i-1]与t[j-1]相等
- 初始化
从递推公式dp[i-1][j-1] + dp[i-1][j];和dp[i][j] = dp[i-1][j]是从上方和左上方推导而来,所以dp[i][0]和dp[0][j]一定要初始化- dp[i][0]表示:以i-1为结尾的s可以随便删除元素,出现空字符串的分数,所以dp[i][0] = 1
- dp[0][j]表示空字符串s可以随便删除元素,出现以j-1为结尾的字符串的个数,所以dp[0][j] = 0
- 遍历顺序从上到下,从左向右
- 打印dp数组
class Solution {
public:
int numDistinct(string s, string t) {
// dp[i][j]: 表示以i-1为结尾的s中有以j-1为结尾的t的个数
vector<vector<uint64_t>> dp(s.size() + 1, vector<uint64_t>(t.size() + 1, 0));
// 递推公式 if(nums[i-1] == nums[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
// 初始化
for(int i = 0; i <= s.size(); ++i) dp[i][0] = 1;
for(int j = 1; j <= t.size(); ++j) dp[0][j] = 0;
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];
}
}
// for(auto vec : dp) {
// for(int val : vec) cout << val << " ";
// cout << endl;
// }
return dp[s.size()][t.size()];
}
};
583.两个字符串的删除操作
class Solution {
public:
int minDistance(string word1, string word2) {
// dp[i][j]: 使得word1的i-1和word2的j-1相等所需的最少操作
vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
// 递推公式:if(word1[i-1] == word2[j-1]) dp[i][j] = dp[i-1][j-1];
// else dp[i][j] = dp[i-1][j-1] + 2;
// 初始化
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);
}
}
return dp[word1.size()][word2.size()];
}
};
72.编辑距离
-
dp[i][j]: 表示word1的i-1位置和word2[j-1]位置相同所需的最小操作次数
-
递推公式
- word1[i-1] == word2[j-1]: dp[i][j] = dp[i-1][j-1]
- word1[i-1] != word2[j-1]
- 考虑word1的i-1位置(将word1的i位置删除):dp[i][j] = dp[i-1][j] + 1;
- 考虑word2的j-1位置(将word2的j位置删除): dp[i][j] = dp[i][j-1] + 1;
- 两个位置的元素都考虑(替换一个元素): dp[i][j] = dp[i-1][j-1] + 1;
-
初始化
- word2为空时,需要将word1调整为和word2相同,需要逐个删除word1中的元素,所以dp[i][0] = i;(有几个元素就执行几次删除操作)
- 同理dp[0][j] = j;
-
遍历顺序:从上到下,从左到右
-
打印dp数组
class Solution {
public:
int minDistance(string word1, string word2) {
// dp[i][j]: 表示word1的i-1位置和word2[j-1]位置相同所需的最小操作次数
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, min(dp[i][j-1] + 1, dp[i-1][j-1] + 1));
}
}
for(auto vec : dp) {
for(int val : vec) cout << val << " ";
cout << endl;
}
return dp[word1.size()][word2.size()];
}
分类:
代码随想录
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?