题目描述:
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
思路:
这道题我们可以统计,s和t的最长公共子序列,然后判断最长公共子序列的长度是否等于字符串s的长度,如果相等的话,return true,else return false;
动规五部曲:
1.dp[i][j]:到下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列长度为dp[i][j]。
注意这里是判断s是否为t的子序列。即t的长度是大于等于s的。
2.递推公式的确定:
在确定递推公式的时候,首先考虑下面两个操作:
if(s[i-1]==t[j-1]) //t中找到一个字符出现在s中也出现了
if(s[i-1]!=t[j-1]) //相当于t要删除元素,继续匹配
dp[i][j-1]+1;因为找到一个一个相同字符,相同子序列长度自然在dp[i-1][j-1]的基础上加1.
if(s[i-1]!=t[j-1]),此时相当于t要删除元素,t如果把当前元素t[j-1]删除,那么dp[i][j]的数值就是看s[i-1]与t[j-2]的比较结果,即:dp[i][j]=dp[i][j-1];
3.初始化:
递推公式可以看出dp[i][j]依赖于dp[i-1][j-1]和dp[i][j-1],所以一定要初始化dp[0][0]和dp[i][0].
4.遍历顺序从上到下,从左到右
class Solution {
public:
bool isSubsequence(string s, string t) {
vector> dp(s.size() + 1, vector(t.size() + 1, 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] + 1;
else dp[i][j] = dp[i][j - 1];
}
}
if (dp[s.size()][t.size()] == s.size()) return true;
return false;
}
};
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)
题目数据保证答案符合 32 位带符号整数范围。
这题和上一题有一些相似之处,只不过这一题不是判断一个字符串是否是另一个字符串的子序列,而是判断一个字符串可以通过删除元素得到几次另一个字符串。
动规五部曲:
1.dp[i][j]及其下标的定义:以i-1结尾的字符串s中出现以j-1结尾的字符串t的次数为dp[i][j]
2.递推公式:判断s[i-1]与t[j-1]如果相等,dp[i][j]就由两部分组成:
-可以是s[i-1]等于t[j-1]
-可以是s[i-2]等于tj-1]
dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
如果不相等:dp[i][j]=dp[i-1][j]。为什么呢?因为如果s[i-2]之前与t[j-1]匹配的话,那么依然保持原状。
3.初始化:根据递推公式我们可知要初始化dp[i][0]和dp[0][j].严格根据定义进行初始化就可以。
dp[i][0]代表:以下标i-1结尾的字符串s中,任意删除元素,构成空字符串的个数,为1.
dp[0][j]代表:以空字符串s,任意删除元素,构成以j-1结尾的字符串的个数,为0.
4.遍历顺序:从上往下,从前往后
class Solution {
public:
int numDistinct(string s, string t) {
vector> dp(s.size() + 1, vector(t.size() + 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];
}
}
}
return dp[s.size()][t.size()];
}
};
思路:
方法1:求出两字符串的最长公共子序列,然后返回两字符串长度和-最长公共子序列*2
方法2:类似于上一题,上一题是删除一个字符串中元素,这一题是两个字符串中元素都需要删除,
动规五部曲:
1.dp[i][j]定义:以i-1结尾的字符串s和以j-1结尾的字符串t想要达到相等,一共要删除的元素数量
2.递推公式:两种情况:1.当判断s[i-1]==t[j-1]时,dp[i][j]=dp[i-1][j-1]
2.如果s[i-1]和t[j-1]不同,则有三种情况:
-dp[i][j]=dp[i-1][j]+1 和 dp[i][j]=dp[i][j-1]+1 和 dp[i][j]=dp[i-1][j-1]+2
三者中取最小。
3.初始化:从递推公式上看dp[i][0]和dp[0][j]都必须初始化。
dp[i][0]=i ; dp[0][j]=j
4.遍历顺序:从上往下,从左到右。
class Solution {
public:
int minDistance(string word1, string word2) {
vector> dp(word1.size() + 1, vector(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()];
}
};