Leetcode 最长递增子序列的个数

java solution

class Solution {
    public int findNumberOfLIS(int[] nums) {
        //边界情况处理
        int n = nums.length;
        if(n <= 1) return n;

        //然后我们创建2个数组, 分别是count数组和length数组,
        //length[i]的含义是以i结尾的子数组的最长递增子序列长度
        //count[i]的含义是以i结尾的子数组的最长递增子序列数量
        int[] length = new int[n];
        int[] count = new int[n];
        Arrays.fill(length, 1); //每个位置结尾的子数组都至少存在长度为1的递增子序列
        Arrays.fill(count, 1);
        
        int maxLen = 1;

        //开始动态规划
        for(int i = 1; i < n; i++) {
            for(int j = 0; j < i; j++) {
                if(nums[j] < nums[i]) {
                    //两种情况
//情况1. 以j结尾的子数组的最长递增子序列在末尾拼接上nums[i]的长度大于以i结尾的子数组的最长递增子序列
                    if(length[j] + 1 > length[i]) {
                        //更新length数组和count数组
                        length[i] = length[j] + 1;
//继承子序列数量,因为nums[i]是直接拼接在以j结尾的子数组的最长递增子序列的末尾
                        count[i] = count[j]; 
                    } else if(length[j] + 1 == length[i]){
//情况2. 以j结尾的子数组的最长递增子序列在末尾拼接上nums[i]的长度等于以i结尾的子数组的最长递增子序列
                        //此时不需要更新leng数组
                        count[i] += count[j];
                    }
                }
            }
            maxLen = Math.max(maxLen, length[i]);
        }

        //然后统计最长递增子序列个数
        int res = 0;

        for(int i = 0; i < n; i++) {
            if(length[i] == maxLen) {
                res += count[i];
            }
        }
        return res; 
    }
}

🧠 题目目标回顾

我们要解决的是:

给定一个未排序的数组 nums,返回 最长严格递增子序列的个数

比如:

  • [1,3,5,4,7] → 最长子序列长度是 4,有两种方案 [1,3,4,7][1,3,5,7] → 输出 2

🔍 算法核心思想 —— 动态规划

我们用 动态规划 来解决。

我们需要记录两样东西:

  1. length[i]:表示以 nums[i] 为结尾的 最长递增子序列的长度
  2. count[i]:表示以 nums[i] 为结尾的 最长递增子序列的数量

初始化:

  • 每个元素自成一个子序列,长度为 1,个数为 1
    Arrays.fill(length, 1);
    Arrays.fill(count, 1);
    

🧩 动态转移逻辑

遍历每一个位置 i,再遍历它前面的每一个位置 j,进行比较:

for (int i = 1; i < n; i++) {
    for (int j = 0; j < i; j++) {
        if (nums[j] < nums[i]) {

nums[j] < nums[i] 时,表示可以在 j 的子序列基础上扩展

🧾 两种情况:


✅ 情况1:找到更长的子序列

if (length[j] + 1 > length[i]) {
    length[i] = length[j] + 1;
    count[i] = count[j]; // 继承数量
}

比如原来 nums[i] 结尾只有长度为 2 的递增序列,但现在通过 nums[j] 找到了长度为 3 的,说明有了更新:

  • 更新 length[i]
  • count[j] 赋给 count[i],因为所有以 j 结尾的最长子序列都可以拼到 i

✅ 情况2:找到等长的子序列

else if (length[j] + 1 == length[i]) {
    count[i] += count[j]; // 增加方案数
}

说明除了之前的拼法,又找到了另一种拼法也能拼出相同长度的最长子序列,因此把 count[j] 加到 count[i]


📌 最后一步:统计所有以最长长度结尾的方案数

我们之前计算了每个位置 i 的最长子序列长度,最后要从中找出最长的,然后把这些位置的方案数加起来:

int res = 0;
for (int i = 0; i < n; i++) {
    if (length[i] == maxLen) {
        res += count[i];
    }
}

⏱️ 时间与空间复杂度

  • 时间复杂度:O(n²),两层循环
  • 空间复杂度:O(n),两个数组 length[]count[]

✅ 举个例子:输入 [1, 3, 5, 4, 7]

inums[i]length[i]count[i]说明
0111初始值
13211→3
25311→3→5
34311→3→4
47421→3→4→7 和 1→3→5→7

最后返回 count=2,因为有两个长度为 4 的递增子序列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值