
先搞懂:什么是「子序列」?
这是新手最容易搞混的点,一定要和「子串」区分开:
- 子串 :必须是数组中连续的一段元素
- 子序列 :可以不连续,但元素的相对顺序不能变
比如数组 [10,9,2,5,3,7,101,18]:
- 子串:
[2,5,3](连续) - 子序列:
[2,3,18](不连续,但顺序不变)
题目要求
找到数组中最长的严格递增子序列的长度
- 严格递增:后一个元素必须大于前一个,不能等于
- 子序列可以不连续,但顺序不能乱
核心思路(一句话讲透)
用 dp[i] 表示:以第 i 个元素结尾的最长递增子序列的长度 。 对于每个元素 nums[i],我们看它前面所有比它小的元素 nums[j](j < i):
- 如果
nums[j] < nums[i],说明nums[i]可以接在nums[j]后面,形成一个更长的子序列 - 这个更长的子序列长度就是
dp[j] + 1 - 我们在所有可能的
dp[j] + 1中取最大值,就是dp[i]的值
所以状态转移方程:
plaintext
dp[i] = max(dp[j] + 1) (其中 0 ≤ j < i 且 nums[j] < nums[i])
完整解题步骤
- 处理边界情况:如果数组为空,直接返回 0
- 初始化 dp 数组 :
- 数组长度为 n(和原数组一样长)
- 所有元素初始化为
1(因为每个元素自己就是一个长度为 1 的子序列)
- 遍历计算 dp 数组 :
- 外层循环:
i从0到n-1(当前要计算的元素) - 内层循环:
j从0到i-1(前面所有的元素)- 如果
nums[j] < nums[i](可以接在后面) - 更新
dp[i] = max(dp[i], dp[j] + 1)
- 如果
- 外层循环:
- 返回结果 :dp 数组中的最大值
- 注意:最长子序列不一定以最后一个元素结尾,所以不能直接返回
dp[n-1]
- 注意:最长子序列不一定以最后一个元素结尾,所以不能直接返回
cpp
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
if(n == 0)
return 0;
vector<int> dp(n,1);//初始值设成1 因为序列最短包括自己 长度为1
for(int i = 0;i<n;i++){
for(int j = 0;j<i;j++){
if(nums[j]<nums[i]){
dp[i] = max(dp[i],dp[j]+1);
}
}
}
return *max_element(dp.begin(),dp.end());
}
};