1. 解析
题目大意,求解最长的递增子序列。类似的还有递增连续子序列,一般求解子序列都会涉及到动态规划,本题也不例外
2. 分析
难点在往后搜索的过程中,如何实时记录每个状态的起始位置和对应的长度,动态规划的一个最重要的特征就是在搜索下一个状态的时候,之前所有相关的状态信息都已经记录好,即待搜索状态依赖上一个状态。用dp[i]表示字符串[0, i]的最长递增子序列长度,为了将之前的所有状态都存储下来,每次利用待检索状态nums[i]与之前的每个状态进行比较,实时更新最优值。另外动态规划每个状态的局部最优解不一定是全局最优解,但全局最优解一定存在所有的局部最优解当中。所以每次得到局部最优解的时候,与当前的最优解进行比较,取最优解。
例如:5 2 4 3
(1) i = 0 nums[i] = 5 dp = [1, 1, 1, 1] res = 1
(2) i = 1 nums[i] = 2 5 > 2 dp = [1, 1, 1, 1] res = 1
(3) i = 2 nums[i] = 4 5 > 4 2 < 4 dp = [1, 1, 2, 1] res = 2
(4) i = 3 nums[i] = 3 5 > 3 2 < 3 4 > 3 dp = [1, 1, 2, 2] res = 2
这就是执行的过程,我们可以看到,每次都会用当前的状态实时更新之前的状态
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int res = 0;
vector<int> dp(nums.size(), 1);
for (int i = 0; i < nums.size(); ++i){
for (int j = 0; j < i; ++j)
if (nums[j] < nums[i]) dp[i] = max(dp[i], dp[j]+1);
res = max(dp[i], res);
}
return res;
}
};
3. 二分查找
这种解法是属于非常规解法,不是很好想,用nums记录所选择的数,每次判断res数组是否存在一个不小于num的数,如果不存在,则为最大的数,肯定满足递增的要求;若存在,将其替换成一个更小的数,保证在序列最长的情况下,所有序列中的元素是最小的,很聪明的做法。缺点是最终所存储元素的结果不是真正的LIS,但长度是不变的
class Solution {
public:
int lengthOfLIS(vector<int>& nums){
vector<int> res;
for (auto num : nums){
auto it = lower_bound(res.begin(), res.end(), num);
if (it == res.end()) res.push_back(num);
else *it = num;
}
return res.size();
}
};
4. 类似题目