目录
题目链接:392.判断子序列
思路
①dp数组,dp[i][j]表示下标为i-1结尾的字符串s和下标j-1结尾的字符串t,相同子序列的长度为dp[i][j]
②递推公式,当上一个字符相等时,dp[i][j] = dp[i-1][j-1] + 1,否则dp[i][j] = dp[i][j-1],不相等时说明s的当前字符未在t中找到,跳过t的当前字符,与t的下一个字符相比较
③dp数组初始化,根据递推公式,dp[0][0] = 0,dp[i][0] = 0,递推公式后的值都要由其进行递推,初始化为0是因为母串t为空串时,公共长度也为0
④遍历顺序,先子串s,后母串t,从前往后
⑤推导dp数组
代码
class Solution {
public:
bool isSubsequence(string s, string t) {
int slen = s.size();
int tlen = t.size();
vector<vector<int>> dp(slen + 1, vector<int>(tlen + 1, 0));
// 终止条件是小于等于,因为字符串的索引是i-1和j-1
for (int i = 1; i <= slen; i++) {
for (int j = 1; j <= tlen; j++) {
// 相等时,长度加一
if (s[i - 1] == t[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
// 不等时,遍历母串t的下一个位置
else {
dp[i][j] = dp[i][j - 1];
}
}
}
// dp数组右下角的值等于子串长度时说明s是t的子序列
if (dp[slen][tlen] == slen)
return true;
return false;
}
};
题目链接:115.不同的子序列
思路
①dp数组,dp[i][j]表示下标以i-1的s在下标以j-1的t中出现的次数为dp[i][j]
②递推公式,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]
③dp数组初始化,dp[i][0] = 1;dp[0][j] = 0,t为空,空字符肯定在s中出现一次;s为空,则出现次数肯定为0
④遍历顺序,从左到右,从上到下
⑤推导dp数组
代码
class Solution {
public:
int numDistinct(string s, string t) {
int slen = s.size(); // 母串长度
int tlen = t.size(); // 子串长度
vector<vector<uint64_t>> dp(slen + 1, vector<uint64_t>(tlen + 1, 0));
for (int i = 0; i < slen; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= slen; i++) {
for (int j = 1; j <= tlen; 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[slen][tlen];
}
};
总结
①子序列问题都是从当前字符相等和不相等两种情况进行讨论,392.判断子序列,相等时长度加一,不等时母串跳过当前元素;115.不同的子序列是连续判断,因此在相等时要考虑匹配当前元素和不匹配当前元素两种情况,不等时也是跳过当前元素
②搞清楚dp数组含义,在初始化时根据递推公式以及dp数组含义进行初始化,这点尤为重要