300. 最长递增子序列
这题主要是要想清楚dp[i]的含义,即dp[i]表示以nums[i]结尾的最长递增子序列的长度。所以我们每次都要把nums[i]和前面的每个dp[i]比较来得到当前最大的长度。即转移方程为
for (int j = 0; j < i; j++) {
dp[i] = max(dp[i], dp[j] + 1);
}
由此代码就比较好写了。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n, 1);
int result = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j])
dp[i] = max(dp[i], dp[j] + 1);
}
if (dp[i] > result)
result = dp[i];
}
return result;
}
};
674. 最长连续递增序列
这题就比较简单了吧,特别在上一题的基础上,这个要求连续,所以每次只需要和前面一个比较即可,也可以用贪心来做,这样空间复杂度会降为O(1).
class Solution {
public:
int findLengthOfLCIS(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n, 1);
int result = 1;
for (int i = 1; i < n; i++) {
if (nums[i] > nums[i - 1])
dp[i] = dp[i - 1] + 1;
result = max(result, dp[i]);
}
return result;
}
};
718. 最长重复子数组
这个题说难也不难,反正dp数组的定义要落在以某个数结尾上来,这里dp[i] [j]表示以nums1的第i个结尾和以nums的第j个结尾的公共子序列最大长度,然后据此进行初始化,转移方程为
if (nums1[i] == nums2[j])
dp[i][j] = dp[i - 1][j - 1] + 1;
代码为:
class Solution {
public:
int findLength(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
vector<vector<int>> dp(m, vector<int>(n, 0));
int ans = 0;
for (int i = 0; i < n; i++) {
if (nums1[0] == nums2[i]) {
dp[0][i] = 1;
ans = 1;
}
}
for (int i = 0; i < m; i++) {
if (nums2[0] == nums1[i]) {
dp[i][0] = 1;
ans = 1;
}
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (nums1[i] == nums2[j])
dp[i][j] = dp[i - 1][j - 1] + 1;
ans = max(ans, dp[i][j]);
}
}
return ans;
}
};
1143. 最长公共子序列
这里的转移方程为:
if (text1[i - 1] != text2[j - 1]) {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
else {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
看起来这道题和前面的这几道题没什么区别,但是我们发现这个转移方程和上一道题的转移方程存在区别,即不相等的情况下多了一个赋值,为什么存在这个区别呢,因为这里要求的是不连续的!
dp[i] [j] = max(dp[i - 1] [j], dp[i] [j - 1])这个式子的含义是如果不相等,把之前相等的较大的赋给dp[i] [j],如果后面还有相等的,那后面相等的就可以加上前面相等的部分,即不管中间不相等的部分因为是不要求连续,如果要求连续的话就不能这么做了!要求连续的话只能在每一次不连续的地方就赋值为0才能保证后面连续相等于前面连续相等的没关系,长度不会加在一起。所以这是一个很重要的区别!
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m = text1.size(), n = text2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m ; i++) {
for (int j = 1; j <= n; j++) {
if (text1[i - 1] != text2[j - 1]) {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
else {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
}
}
return dp[m][n];
}
};
1035. 不相交的线
这个题只要发现其实就是求两个数组的最长公共子序列就比较简单了。和那个代码一模一样。
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (nums1[i - 1] == nums2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[m][n];
}
};
115. 不同的子序列
这个题主要要想通转移方程,dp[i] [j]表示以i-1为结尾的s子序列中出现以j-1为结尾的t的个数。转移方程也要分两种情况考虑,一种是s[i] == t[j],这种情况下dp[i] [j] 又可能从dp[i - 1] [j - 1]得来,也可能从dp[i - 1] [j]得来,因为不一定要用到是s[i]。另一种就是s[i] != t[j],这种情况下只有从dp[i - 1] [j]。
class Solution {
public:
int numDistinct(string s, string t) {
int m = s.size(), n = t.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 0; i <= m; i++)
dp[i][0] = 1;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (s[i - 1] == t[j - 1] && (long long)dp[i - 1][j - 1] + dp[i - 1][j] <= INT_MAX) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
}
else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[m][n];
}
};