115.不同的子序列(只能删除s)
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)
题目数据保证答案符合 32 位带符号整数范围。
提示:
- 0 <= s.length, t.length <= 1000
- s 和 t 由英文字母组成
思路
1.dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。
2.乍一看挺像爬楼梯的方法数
- 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] = dp[i - 1][j];(好理解,不匹配则删掉一个s,继续看前一个)
- // 当此元素相等时, 则求i-1个s前面有多少个"ba" + i-1个s前面有多少个完整"bag" // 若此元素不相等, 则求s的前面有多少个"bag"
假设当前遍历到的 t 是 bag,s 是 babga,此时 s 又来了一个 g, 和当前 t 的最后一个元素相同了。这个时候新的 babgag 含有 bag 的数量||是在 babga 原本包含的 bag 数量(dp【i - 1】【j】)的基础上,增加了使用新来的 g 新组成的 bag 数量。新来的 g 能组成多少个bag,其实是原本的 babga 包含多少个 ba。也就是 dp【i - 1】【j - 1】。
3.初始化,dp[0][0]=0;其他第一列都为0(因为和空字符的交集都是1)第一行都是0,因为空的s中永远只有0个t。
4.递推方向
5.举例
代码
class Solution {
public:
int numDistinct(string s, string t) {
//dp表示前i-1的s中有多少个前j-1的t
vector<vector<uint64_t>>dp(s.size()+1,vector<uint64_t>(t.size()+1));
for(int i=0;i<s.size();i++)dp[i][0]=1;//初始化,和空字符的交集个数是1
for(int j=1;j<t.size();j++)dp[0][j]=0;//0个s和t的交集是0,例外,dp[0][0]=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];
}
}
return dp[s.size()][t.size()];
}
};
583. 两个字符串的删除操作
583. 两个字符串的删除操作 - 力扣(LeetCode)
给定两个单词
word1
和word2
,返回使得word1
和word2
相同所需的最小步数。每步 可以删除任意一个字符串中的一个字符。
示例 1:
输入: word1 = "sea", word2 = "eat" 输出: 2 解释: 第一步将 "sea" 变为 "ea" ,第二步将 "eat "变为 "ea"示例 2:
输入:word1 = "leetcode", word2 = "etco" 输出:4
写法一(记录删除次数)
真的很像爬楼梯和装背包,应该说是01背包吧。都有前面的基础上加上有新元素的方法。
1.dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。
2.
- 当word1[i - 1] 与 word2[j - 1]相同的时候
- 当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],最少操作次数为dp[i - 1][j] + 1
情况二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1
情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2
那最后当然是取最小值,所以当word1[i - 1] 与 word2[j - 1]不相同的时候,递推公式:dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});
因为 dp[i][j - 1] + 1 = dp[i - 1][j - 1] + 2,所以递推公式可简化为:dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
3.初始化操作。若一个为空,则要删除另一个的大小(次数)才能相同。所以第一行全是j,第一列全是i。
5.举例
代码
class Solution {
public:
int minDistance(string word1, string word2) {
vector<vector<int>>dp(word1.size()+1,vector<int>(word2.size()+1));
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()];
}
};
写法二(记录最大公共长度,间接法)
最后统计剩余元素个数即可。
代码
class Solution {
public:
int minDistance(string word1, string word2) {
vector<vector<int>>dp(word1.size()+1,vector<int>(word2.size()+1));
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]+1;//记录相同元素
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);//不相同,则比较前面哪个重合最多元素
}
}
return word1.size()+word2.size()-dp[word1.size()][word2.size()]*2;//得删除元素个数
}
};
72. 编辑距离(都是为了本题服务)
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
示例 1:
输入:word1 = "horse", word2 = "ros"
输出:3
解释: horse -> rorse (将 'h' 替换为 'r') rorse -> rose (删除 'r') rose -> ros (删除 'e')
示例 2:
输入:word1 = "intention", word2 = "execution"
输出:5
解释: intention -> inention (删除 't') inention -> enention (将 'i' 替换为 'e') enention -> exention (将 'n' 替换为 'x') exention -> exection (将 'n' 替换为 'c') exection -> execution (插入 'u')
思路
做过了前面题目,本题真不难
1.dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。
2.
·if (word1[i - 1] == word2[j - 1])
那么说明不用任何编辑,dp[i][j]
就应该是 dp[i - 1][j - 1]
,即dp[i][j] = dp[i - 1][j - 1];
·if (word1[i - 1] != word2[j - 1])
,此时就需要编辑了,如何编辑呢?
- 操作一:word1删除一个元素,那么就是以下标i - 2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 再加上一个操作。
即 dp[i][j] = dp[i - 1][j] + 1;
- 操作二:word2删除一个元素,那么就是以下标i - 1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 再加上一个操作。(相当于word1添加元素)
即 dp[i][j] = dp[i][j - 1] + 1;
·操作三:替换元素,操作加1.所以 dp[i][j] = dp[i - 1][j - 1] + 1;
然后取三者最小值。
初始值和遍历顺序与前面一样操作。
举例
代码
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(min(dp[i-1][j-1]+1,dp[i-1][j]+1),dp[i][j-1]+1);//替换操作,删除操作,插入操作(删除数组2的元素相当于插入元素进入数组1)
}
}
}
return dp[word1.size()][word2.size()];
}
};