一、题目打卡
1.1 判断子序列
题目链接:. - 力扣(LeetCode)
class Solution {
public:
bool isSubsequence(string s, string t) {
vector<vector<int>> dp(t.size() + 1, vector<int>(s.size() + 1, 0));
for(int i = 1; i < t.size() + 1; i++){
for(int j = 1 ; j < s.size() + 1; j++){
if(s[j-1] == t[i-1]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
// for(int i = 0 ; i <= t.size();i++){
// for(int j = 0 ; j <= s.size();j++){
// cout << dp[i][j] << " ";
// }
// cout << endl;
// }
return dp[t.size()][s.size()] == s.size();
}
};
弄清楚这个题目的状态定义:dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。
然后状态转移就分当前的两个序列当前值相等和不相等两种情况进行处理,其实这里相等,也可以像背包问题一样理解,也就是取用了这个数字,所以就不能从左和上面继续取值,此时就应该取左上角的状态转移过来,另一个情况则是不取用当前这个值,然后从状态的左边和上面转移而来。
1.2 不同的子序列
题目链接:. - 力扣(LeetCode)
class Solution {
public:
int numDistinct(string t, string s) {
// vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));
vector<vector<unsigned long long>> dp(s.size(), vector<unsigned long long>(t.size(), 0));
if(s[0] == t[0]) dp[0][0] = 1;
// 这个是不用初始化的,因为如果s的数量都比t大了,那么是不可能用t组成s的
// for(int i = 1;i < s.size();i++){
// if(s[i] == t[0]) dp[i][0] = dp[i-1][0] + 1;
// else dp[i][0] = dp[i-1][0];
// }
for(int i = 1 ; i < t.size();i++){
if(t[i] == s[0]) dp[0][i] = dp[0][i - 1] + 1;
else dp[0][i] = dp[0][i - 1];
}
for(int i = 1; i < s.size();i++){
for(int j = 1;j < t.size();j++){
if(s[i] == t[j]) dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];
else dp[i][j] = dp[i][j - 1];
}
}
// for(int i = 0; i < s.size();i++){
// for(int j = 0 ; j < t.size();j++){
// cout << dp[i][j] << " ";
// }
// cout << endl;
// }
return dp[s.size() - 1][t.size() - 1];
}
};
首先还是把dp数组的定义弄明白,如果是我这样定义的话,dp[i][j]就是以下标 i 为结尾的字符串 t 和以下标 j 未结尾的字符串 s,相同子序列出现的最大次数。
然后画了些图:
上面基本就是我思路了,这个题目我不咋习惯用i-1索引的方式,虽然这样初始化麻烦了一点,但是我觉得比较好理解,递推的那个过程,其实也是一样的,碰到相等的,那就可以进行新的组合,那么新的组合的前面就应该来自于左上角的部分,而因为这个统计的是个数,所以还应该加上不使用当前这个值所统计的个数,也就是来自左边;另一种情况是,不相等,那么就直接继承左边就可以了。