目录
392. 判断子序列
题目描述:
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"
是"abcde"
的一个子序列,而"aec"
不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
示例 1:
输入:s = "abc", t = "ahbgdc" 输出:true
示例 2:
输入:s = "axc", t = "ahbgdc" 输出:false
提示:
0 <= s.length <= 100
0 <= t.length <= 10^4
- 两个字符串都只由小写字符组成。
实现代码与解析:
双指针
class Solution {
public:
bool isSubsequence(string s, string t) {
int i = 0, j = 0;
while(i < s.size() && j < t.size())
{
if (s[i] == t[j])
{
i++;
j++;
}
else j++;
}
if (i == s.size()) return true;
else return false;
}
};
原理思路:
双指针,较简单。
动态规划:
class Solution {
public:
bool isSubsequence(string s, string t) {
vector<vector<int>> f(s.size() + 1, vector<int>(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]) f[i][j] = f[i - 1][j - 1] + 1;
else f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
if (f[s.size()][t.size()] == s.size()) return true;
return false;
}
};
原理思路:
与求两字符串最长公共子序列相同, 只不过这里是判断一个字符串是不是另一个字符串的子序列而已。dp[i][j]数组含义为s中前 i - 1,t中 j - 1 前(包含 j - 1)的最长公共子序列。
所以
else f[i][j] = max(f[i - 1][j], f[i][j - 1]);
也可以改为:
else f[i][j] = f[i][j - 1];
这时dp数组的含义就变为了s中以 i - 1为结尾串和t中 j - 1 前(包含 j - 1)的最长公共子序列。也可以解决问题。
115. 不同的子序列
题目描述:
给你两个字符串 s
和 t
,统计并返回在 s
的 子序列 中 t
出现的个数。
题目数据保证答案符合 32 位带符号整数范围。
示例 1:
输入:s = "rabbbit", t = "rabbit"
输:3
解释:
如下所示, 有 3 种可以从 s 中得到 "rabbit" 的方案。
rabbbit、rabbbit、rabbbit
示例 2:
输入:s = "babgbag", t = "bag"
输出:5
解释:
如下所示, 有 5 种可以从 s 中得到 "bag" 的方案。
babgbag、babgbag、babgbag、babgbag、babgbag
提示:
1 <= s.length, t.length <= 1000
s
和t
由英文字母组成
实现代码与解析:
动态规划
class Solution {
public:
int numDistinct(string s, string t) {
vector<vector<unsigned long long>> f(s.size() + 1, vector<unsigned long long>(t.size() + 1, 0));
// 初始化
for (int i = 0; i < s.size(); i++) f[i][0] = 1;
for (int j = 1; j < t.size(); j++) f[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]) f[i][j] = f[i - 1][j - 1] + f[i - 1][j];
else f[i][j] = f[i - 1][j];
return f[s.size()][t.size()];
}
};
原理思路:
沿用前一题的思想,我们dp[i][j]数组的含义为s的下标 i - 1(包含i - 1),t 中以 j - 1结尾的串中公共子序列的个数。
根据dp数组的含义写出递推式:
·当 s[i-1] == t[j-1]时,f[ i ][ j ] = f[ i - 1 ][ j - 1] + f[ i - 1][ j ] ,两种情况,j 可以选择和 i 匹配,也可以选择不和 i 匹配和 i 前面的匹配,因为这里要计算个数嘛,所以要相加起来。
·当 s[i-1] != t[j-1] 时, f[ i ][ j ] = f[ i - 1][ j ], 既然不相等,那么只能选择和 i 前面的去匹配。
本题最重要的是dp数组的初始化:
·当 j = 0 时,说明 t 为空,所以 s 中一个都不选才能为空这一种情况,所以赋值为1。
·当 i = 0 时,说明 s 为空,t 无论如何都不可能形成空,所以赋值为0。
·当 i 和 j 都 等于 0 时,显然也是 1。
可以根据dp数组含义好好思考一下。