题目列表
代码随想录地址:https://programmercarl.com/0392.%E5%88%A4%E6%96%AD%E5%AD%90%E5%BA%8F%E5%88%97.html
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"
是"abcde"
的一个子序列,而"aec"
不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
致谢:
特别感谢 @pbrother 添加此问题并且创建所有测试用例。
示例 1:
输入:s = "abc", t = "ahbgdc"
输出:true
示例 2:
输入:s = "axc", t = "ahbgdc"
输出:false
提示:
0 <= s.length <= 100
动态规划五部曲:
dp[i] [j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i] [j]。
if (s[i - 1] == t[j - 1]),那么dp[i] [j] = dp[i - 1] [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]。
全部初始化为0。
从上到下从左到右。
草稿纸。
时间复杂度:O(m + n)
空间复杂度:O(1)
/*
* @lc app=leetcode.cn id=392 lang=cpp
*
* [392] 判断子序列
*/
// @lc code=start
class Solution {
public:
bool isSubsequence(string s, string t) {
int m = s.size();
int n = t.size();
//双指针的方法,两个指针分别从s和t左往右移
int i = 0, j = 0;
while(i < m && j < n)
{
if(s[i] == t[j])
{
i++;
j++;
}
else
j++;
}
if(i == m)
return true;
return false;
}
};
// @lc code=end
/*
* @lc app=leetcode.cn id=392 lang=cpp
*
* [392] 判断子序列
*/
// @lc code=start
class Solution {
public:
bool isSubsequence(string s, string t) {
int sSize = s.size();
int tSize = t.size();
//定义和初始化dp数组
vector<vector<int>> dp(sSize + 1, vector<int>(tSize + 1, 0));
//遍历
for(int i = 1; i <= sSize; i++)
{
for(int j = 1; j <= tSize; 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[sSize][tSize] == sSize)
return true;
return false;
}
};
// @lc code=end
代码随想录地址:https://programmercarl.com/0115.%E4%B8%8D%E5%90%8C%E7%9A%84%E5%AD%90%E5%BA%8F%E5%88%97.html
给定一个字符串 s
和一个字符串 t
,计算在 s
的子序列中 t
出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE"
是 "ABCDE"
的一个子序列,而 "AEC"
不是)
题目数据保证答案符合 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
提示:
0 <= s.length, t.length <= 1000
s
和 t
由英文字母组成动态规划五部曲:
dp[i] [j]:以i-1为结尾的s子序列 出现 以j-1为结尾的t的个数 为dp[i] [j]。
这一类问题,基本是要分析两种情况
当s[i - 1] 与 t[j - 1]相等时,dp[i] [j]可以有两部分组成。
s[i - 1] [j - 1]:表示以i - 2为结尾的s子序列 出现 以j -2为结尾 的个数
s[i - 1] [j]:表示以i - 2为结尾的s子序列 出现 以j - 1为结尾 的个数
假设字符有重复,那么对于字符串t的组成有两种选择,第一种选择当前的i - 1字符,第二种选择前面的字符;具体存不存在重复字符,交给dp数组来递推表示。
例如: s:bagg 和 t:bag ,s[3] 和 t[2]是相同的,但是字符串s也可以不用s[3]来匹配,即用s[0]s[1]s[2]组成的bag。
当然也可以用s[3]来匹配,即:s[0]s[1]s[3]组成的bag。
所以当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[i] [0]:s中的空字符串可以组成一个空字符串,且只有一种情况。所以dp[i] [0] = 1。(i可以等于0)
dp[0] [j]:s中的空字符串不能组成字符串t,所以dp[0] [j] = 0。(j不可以等于0)
从上到下从左到右。
使用二维数组:
/*
* @lc app=leetcode.cn id=115 lang=cpp
*
* [115] 不同的子序列
*/
// @lc code=start
class Solution {
public:
int numDistinct(string s, string t) {
//定义dp数组 使用int会over flow
vector<vector<uint64_t>> dp(s.size() + 1, vector<uint64_t>(t.size() + 1));
//初始化dp数组
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] + dp[i - 1][j - 1];
else
dp[i][j] = dp[i - 1][j];
}
return dp[s.size()][t.size()];
}
};
// @lc code=end
使用一维数组:
/*
* @lc app=leetcode.cn id=115 lang=cpp
*
* [115] 不同的子序列
*/
// @lc code=start
class Solution {
public:
int numDistinct(string s, string t) {
//定义dp数组 使用int会over flow
vector<uint64_t> dp(t.size() + 1);
//初始化dp数组
dp[0] = 1;
for(int i = 1; i <= t.size(); i++)
dp[i] = 0;
//遍历
for(int i = 1; i <= s.size(); i++)
for(int j = t.size(); j >= 1; j--)
{
//当前字符相等的情况
if(s[i - 1] == t[j - 1])
dp[j] = dp[j] + dp[j - 1];
else
dp[j] = dp[j];
}
return dp[t.size()];
}
};
// @lc code=end