#P4094. 最长递增子序列
-
ID: 2331
Tried: 122
Accepted: 39
Difficulty: 6
-
算法标签>动态规划
最长递增子序列
题目内容
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7]是数组 [0,3,1,6,2,2,7] 的子序列。
输入描述
一个整数数组 nums
输出描述
最长严格递增子序列的长度
样例1
输入
10 9 2 5 3 7 101 18
输出
4
说明
最长递增子序列是 [2,3,7,101],因此长度为 4。
样例2
输入
0 1 0 3 2 3
输出
4
样例3
输入
7 7 7 7 7 7 7
输出
1
提示:
- 1<=nums.length<=2500
- −104<=nums[i]<=104
题解
题面描述
给定一个整数数组 nums,要求找到其中最长严格递增子序列的长度。子序列可以通过删除数组中的部分元素(也可以不删除)得到,且保留原有顺序。
思路
可以使用动态规划(DP)或者贪心+二分查找的方法来求解。这里介绍贪心+二分查找的思路,时间复杂度为 O(nlogn)。
- 我们维护一个数组 dp,其中 dp[i] 表示长度为 i+1 的递增子序列的最后一个元素的最小可能值。
- 遍历数组 nums,对于每个元素 x,使用二分查找在 dp 中找到第一个大于或等于 x 的位置:
- 如果 x 比所有 dp 元素都大,则将 x 添加到 dp 的末尾;
- 否则用 x 替换找到的那个位置上的值,使得后续更容易构造更长的序列。
- 最终 dp 数组的长度即为最长严格递增子序列的长度。
代码分析
- 时间复杂度:遍历 nums 的每个元素为 O(n),二分查找的时间复杂度为 O(logn),总时间复杂度为 O(nlogn)。
- 空间复杂度:O(n) 用于存储 dp 数组。
C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
// 求最长严格递增子序列的长度
int lengthOfLIS(vector<int>& nums) {
// dp 数组,用于保存长度为 $i+1$ 的递增子序列最后一个元素的最小值
vector<int> dp;
for (int num : nums) {
// 使用二分查找在 dp 中找到第一个大于或等于 num 的位置
auto it = lower_bound(dp.begin(), dp.end(), num);
if (it == dp.end()) {
// 如果没有找到,则将 num 加入到 dp 的末尾
dp.push_back(num);
} else {
// 找到则用 num 替换当前元素
*it = num;
}
}
// dp 的长度即为最长严格递增子序列的长度
return dp.size();
}
};
int main() {
vector<int> nums;
int x;
// 读入所有整数,输入以空格隔开
while (cin >> x) {
nums.push_back(x);
}
Solution sol;
int ans = sol.lengthOfLIS(nums);
cout << ans << endl;
return 0;
}
Python
# 定义 Solution 类
class Solution:
def lengthOfLIS(self, nums: list) -> int:
# dp 列表用于保存长度为 $i+1$ 的递增子序列最后一个元素的最小值
dp = []
# 遍历每个数字
for num in nums:
# 使用二分查找找到 dp 中第一个大于或等于 num 的位置
left, right = 0, len(dp)
while left < right:
mid = (left + right) // 2
if dp[mid] < num:
left = mid + 1
else:
right = mid
# 如果 left 等于 dp 长度,则说明 num 大于所有元素
if left == len(dp):
dp.append(num)
else:
# 否则用 num 替换找到的位置上的元素
dp[left] = num
# dp 的长度即为最长严格递增子序列的长度
return len(dp)
if __name__ == "__main__":
import sys
# 读入所有输入,假设输入数字以空格隔开
data = sys.stdin.read().strip().split()
nums = list(map(int, data))
sol = Solution()
print(sol.lengthOfLIS(nums))
Java
import java.util.*;
import java.io.*;
public class Main {
// 定义 Solution 类
static class Solution {
// 求最长严格递增子序列的长度
public int lengthOfLIS(int[] nums) {
// dp 数组用于保存长度为 $i+1$ 的递增子序列最后一个元素的最小值
ArrayList<Integer> dp = new ArrayList<>();
for (int num : nums) {
// 二分查找在 dp 中寻找第一个大于或等于 num 的位置
int left = 0, right = dp.size();
while (left < right) {
int mid = (left + right) / 2;
if (dp.get(mid) < num) {
left = mid + 1;
} else {
right = mid;
}
}
// 如果 left 等于 dp 的长度,则将 num 添加到末尾
if (left == dp.size()) {
dp.add(num);
} else {
// 否则用 num 替换 dp 中对应位置的元素
dp.set(left, num);
}
}
// dp 的长度即为最长严格递增子序列的长度
return dp.size();
}
}
public static void main(String[] args) throws IOException {
// 使用 BufferedReader 读入输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
// 使用空格分割输入的字符串
String[] parts = line.trim().split("\\s+");
int n = parts.length;
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = Integer.parseInt(parts[i]);
}
Solution sol = new Solution();
int ans = sol.lengthOfLIS(nums);
// 输出结果
System.out.println(ans);
}
}