目录
最长递增子序列
摆动序列
最长递增子序列的个数
最长数对链
最长定差子序列
最长的斐波那契子序列的长度
最长等差数列
等差数列划分 II - 子序列
最长递增子序列
300. 最长递增子序列
子数组是连续的,子序列可以不连续,那么就要去[0, i - 1] 区间找
参考代码
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n, 1);
int ret = 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);
}
ret = max(ret, dp[i]);
}
return ret;
}
};
摆动序列
376. 摆动序列
错误: g[i] = max(g[i], f[j] + 1);这个地方写错成f[i],导致逻辑错误
参考代码
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
vector<int> f(n, 1), g(n, 1);
int ret = 1;
for(int i = 1; i < n; i++)
{
for(int j = 0; j < i; j++)
{
if(nums[j] < nums[i]) f[i] = max(f[i], g[j] + 1);
else if(nums[j] > nums[i]) g[i] = max(g[i], f[j] + 1);
}
ret = max(ret, max(f[i], g[i]));
}
return ret;
}
};
最长递增子序列的个数
673. 最长递增子序列的个数 ☆☆☆☆☆
逻辑其实差不多,我们需要用len来更新count,如果只表示一个count的话没有len,没法更新count;只有在nums[j] < nums[i] 的时候才能操作,这是递增的基本条件;然后通过长度来更新;大于自然要重置,等于则延续count[j];通过for j 循环找出 i 位置为结尾的最长长度和最大个数
参考代码
class Solution {
public:
int findNumberOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> len(n, 1), count(n, 1);
int maxlen = 1, retcount = 1;
for(int i = 1; i < n; i++)
{
for(int j = 0; j < i; j++)
{
if(nums[j] < nums[i])
{
if(len[j] + 1 == len[i]) count[i] += count[j];
else if(len[j] + 1 > len[i])
len[i] = len[j] + 1, count[i] = count[j];
}
}
if(len[i] > maxlen)
maxlen = len[i], retcount = count[i];
else if(len[i] == maxlen)
retcount += count[i];
}
return retcount;
}
};
最长数对链
646. 最长数对链
去[0, i - 1] 里面找符合条件的,再比较
参考代码
class Solution {
public:
int findLongestChain(vector<vector<int>>& pairs) {
int n = pairs.size();
sort(pairs.begin(), pairs.end());
vector<int> dp(n, 1);
int ret = 1;
for(int i = 1; i < n; i++)
{
for(int j = 0; j < i; j++)
{
if(pairs[j][1] < pairs[i][0])
dp[i] = max(dp[i], dp[j] + 1);
}
ret = max(ret, dp[i]);
}
return ret;
}
};
最长定差子序列
1218. 最长定差子序列
最开始,老样子n方,但是发现超时;
class Solution5_1 {
public:
int longestSubsequence(vector<int>& arr, int difference) {
int n = arr.size();
vector<int> dp(n, 1);
int ret = 1;
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (arr[j] + difference == arr[i]) dp[i] = max(dp[i], dp[j] + 1);
}
ret = max(ret, dp[i]);
}
return ret;
}
};
然后发现只要找最后一个倒找,找到最后一个满足差值的就行;后面才发现,如果一个数据很长而且都满足条件的很少,那么这个时候优化就没有用了,
class Solution5_2 {
public:
int longestSubsequence(vector<int>& arr, int difference) {
int n = arr.size();
vector<int> dp(n, 1);
int ret = 1;
for (int i = 1; i < n; i++)
{
for (int j = i - 1; j >= 0; j--)
{
if (arr[j] + difference == arr[i])
{
dp[i] = dp[j] + 1;
break;
}
}
ret = max(ret, dp[i]);
}
return ret;
}
};
所以我们采用hash<元素, 满足条件的个数>来映射
参考代码
class Solution {
public:
int longestSubsequence(vector<int>& arr, int difference) {
unordered_map<int, int> hash;
int n = arr.size(), ret = 1;
hash[arr[0]] = 1;
for(int i = 1; i < n; i++)
{
hash[arr[i]] = hash[arr[i] - difference] + 1;
ret = max(ret, hash[arr[i]]);
}
return ret;
}
};
最长的斐波那契子序列的长度
873. 最长的斐波那契子序列的长度
如果是一维的状态表示:以i位置为结尾的最长斐波那契序列的长度,那么它的更新条件就要前面两个数,又要遍历两遍,但也不对,没法通过前面的状态来改变当前位置的状态;
所以用结尾两个位置来锁定这个斐波那契序列;自然就是二维dp
且是严格递增,hash可以直接全写出来(也可以一步一步定义),且是<int , int> 每个元素只有一个对应的下标
注意:最后一步 oj题没有这时候只会报错,
参考代码
class Solution {
public:
int lenLongestFibSubseq(vector<int>& arr) {
int n = arr.size();
vector<vector<int>> dp(n, vector<int>(n, 2));
unordered_map<int, int> hash;
for(int i = 0; i < n; i++)
hash[arr[i]] = i;
int ret = 2;
for(int j = 2; j < n; j++)
{
for(int i = 1; i < j; i++)
{
int num = arr[j] - arr[i];
if(hash.count(num) && hash[num] < i)
dp[i][j] = dp[hash[num]][i] + 1;
ret = max(ret, dp[i][j]);
}
}
return ret < 3 ? 0 : ret;
}
};
一步步定义参考代码
class Solution {
public:
int lenLongestFibSubseq(vector<int>& arr) {
int n = arr.size();
vector<vector<int>> dp(n, vector<int>(n, 2));
unordered_map<int, int> hash;
// for(int i = 0; i < n; i++)
// hash[arr[i]] = i;
hash[arr[0]] = 0;
int ret = 2;
for (int i = 1; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
int num = arr[j] - arr[i];
if (hash.count(num) && hash[num] < i)
dp[i][j] = dp[hash[num]][i] + 1;
ret = max(ret, dp[i][j]);
}
hash[arr[i]] = i;
}
return ret < 3 ? 0 : ret;
}
};
最长等差数列
1027. 最长等差数列
以为我们需要元素对应的下标,所以用哈希表,
第二,这题并不是严格递增,那就是很可能会有相同的数,如果一次定义完哈希值,那么相同元素的下标就会用后面的那个,但是也可以定义为<int, vector<int>>
其三:每一次的 i j 结尾都是不同的,所以ret必须要放在第二层循环里
注意 ret = 2, 题目是length >= 2 ,且两个数也被认为是构成等差数列
代码如下
class Solution {
public:
int longestArithSeqLength(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> dp(n, vector<int>(n, 2));
int ret = 2;
unordered_map<int, vector<int>> hash;
for(int i = 0; i < n; i++)
hash[nums[i]].push_back(i);
for(int j = 2; j < n; j++)
{
for(int i = 1; i < j; i++)
{
int num = 2 * nums[i] - nums[j];
// if(hash.count(num) && hash[num] < i)//不能这么写了因为hash[num]是vector<int>
if(hash.count(num))
for(auto e : hash[num])
if(e < i)
dp[i][j] = max(dp[i][j], dp[e][i] + 1);
ret = max(ret, dp[i][j]);
}
}
return ret;
}
};
和 -> 最长定差子序列 想法类似,num只要找最接近 i 的就行,可以理解为覆盖掉原来的距离 i 较远元素的下标
参考代码
class Solution {
public:
int longestArithSeqLength(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> dp(n, vector<int>(n, 2));
int ret = 2;
unordered_map<int, int> hash;
hash[nums[0]] = 0;
for(int i = 1; i < n - 1; i++)
{
for(int j = i + 1; j < n; j++)
{
int num = 2 * nums[i] - nums[j];
if(hash.count(num) && hash[num] < i)
dp[i][j] = dp[hash[num]][i] + 1;
ret = max(ret, dp[i][j]);
}
hash[nums[i]] = i;
}
return ret;
}
};
等差数列划分 II - 子序列
446. 等差数列划分 II - 子序列
思路: 题目求的是个数,那么dp表要初始化成0,既然是子序列 个数 那么就是满足条件的所有下标(e),这里不能覆盖下标,要找到所有下标,所以hash是int 和vector<int> 对应,
两个i j for的顺序只对覆盖hash有影响作用
参考代码
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> dp(n, vector<int>(n));
unordered_map<long long, vector<int>> hash;
for(int i = 0; i < n; i++)
hash[nums[i]].push_back(i);
int ret = 0;
for(int i = 1; i < n - 1; i++)
{
for(int j = i + 1; j < n; j++)
{
long long num = (long long)2 * nums[i] - nums[j];
if(hash.count(num))
for(auto e : hash[num])
if(e < i)
dp[i][j] += dp[e][i] + 1;
ret += dp[i][j];
}
}
return ret;
}
};