leetcode 子序列问题
一般来说,这类问题都是让你求一个最长子序列,因为最短子序列就是一个字符嘛,没啥可问的。一旦涉及到子序列和最值,那几乎可以肯定,考察的是动态规划技巧,时间复杂度一般都是 O(n^2)。
原因很简单,你想想一个字符串,它的子序列有多少种可能?起码是指数级的吧,这种情况下,不用动态规划技巧,还想怎么着?
既然要用动态规划,那就要定义 dp 数组,找状态转移关系。我们说的两种思路模板,就是 dp 数组的定义思路。不同的问题可能需要不同的 dp 数组定义来解决。
涉及两个字符串/数组时(比如最长公共子序列),dp 数组的含义如下:
在子数组 arr1[0..i] 和子数组 arr2[0..j] 中,我们要求的子序列(最长公共子序列)长度为 dp[i][j]。
2.2 只涉及一个字符串/数组时(比如本文要讲的最长回文子序列),dp 数组的含义如下:
在子数组 array[i..j] 中,我们要求的子序列(最长回文子序列)的长度为 dp[i][j]
1. 最长回文子序列
给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。
示例 1:
输入:
"bbbab"
输出:
4
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length();
int[][] dp = new int[n][n];
for (int i = 0; i < n; i++)
dp[i][i] = 1;
for (int i = n - 2; i >= 0; i--) {
for (int j = i + 1; j < n; j++) {
if (s.charAt(i) == s.charAt(j))
dp[i][j] = dp[i + 1][j - 1] + 2;
else
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
return dp[0][n-1];
}
}
2. 所有递增子序列(491)
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
示例:
输入: [4, 6, 7, 7]
输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
说明:
给定数组的长度不会超过15。
数组中的整数范围是 [-100,100]。
给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。
//直接回溯
class Solution {
Set<List<Integer>> res;
int[] nums;
int n;
public List<List<Integer>> findSubsequences(int[] nums) {
this.nums = nums;
this.n = n;
res = new HashSet<>();
helper(new ArrayList<Integer>(), 0);
return new ArrayList(res);
}
void helper(List<Integer> cur, int start) {
if (cur.size() >= 2) {
res.add(new ArrayList<Integer>(cur));
}
for (int i = start; i < nums.length; i ++) {
if (cur.size() == 0 || cur.get(cur.size() - 1) <= nums[i]) {
cur.add(nums[i]);
helper(cur, i + 1);
cur.remove(cur.size() - 1);
}
}
}
}
//时间优化
//https://leetcode-cn.com/problems/increasing-subsequences/solution/dfs-ctong-su-yi-dong-de-jie-ti-si-lu-by-guanzhanyi/
3. 最长上升子序列(300)
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
if(n == 0 || n == 1) return n;
//dp[i]表示包含nums[i]的最长子串
int[] dp = new int[n];
dp[0] = 1;
for(int i = 0;i < n;i++){
int max = 1;
for(int j = 0;j < i;j++){
if(nums[i] > nums[j]){
max = Math.max(max,dp[j] + 1);
}
}
dp[i] = max;
}
Arrays.sort(dp);
return dp[n-1];
}
}
4. 最长上升子序列的个数(673)
//再维护一个数组,以counter[i]表示包含nums[i]的最长子串的个数
class Solution {
public int findNumberOfLIS(int[] nums) {
if (nums == null || nums.length == 0) return 0;
int n = nums.length;
int[] dp = new int[n];
int[] counter = new int[n];
Arrays.fill(dp, 1);
Arrays.fill(counter, 1);
int max = 0;
for(int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
if (dp[j] + 1 > dp[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
counter[i] = counter[j];
} else if (dp[j] + 1 == dp[i]) {
counter[i] += counter[j];
}
}
}
max = Math.max(max, dp[i]);
}
int res = 0;
for (int i = 0; i < n; i++) {
if (dp[i] == max) res += counter[i];
}
return res;
}
}
未经作者同意请勿转载
本文来自博客园作者:aixueforever,原文链接:https://www.cnblogs.com/aslanvon/articles/13207736.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)