【Lintcode】1093. Number of Longest Increasing Subsequence

题目地址:

https://www.lintcode.com/problem/number-of-longest-increasing-subsequence/description

给定一个数组 A A A,求其最长严格单调增子序列的个数。

思路是动态规划。以下将”最长严格单调增子序列“简称为”最长上升子序列“。设 f [ i ] f[i] f[i]是一个数对,其中 f [ i ] [ 0 ] f[i][0] f[i][0]是指以 A [ i ] A[i] A[i]结尾的最长上升子序列的长度, f [ i ] [ 1 ] f[i][1] f[i][1]是指以 A [ i ] A[i] A[i]结尾的最长上升子序列的数量。则有: f [ i ] [ 0 ] = max ⁡ { max ⁡ j < i { f [ j ] [ 0 ] } + 1 , 1 } f [ i ] [ 1 ] = max ⁡ { ∑ f [ j ] [ 0 ] = f [ i ] [ 0 ] − 1 f [ j ] [ 1 ] , 1 } f[i][0]=\max\{\max_{j<i}\{f[j][0]\}+1,1\}\\f[i][1]=\max\{\sum_{f[j][0]=f[i][0]-1} f[j][1], 1\} f[i][0]=max{j<imax{f[j][0]}+1,1}f[i][1]=max{f[j][0]=f[i][0]1f[j][1],1}注意,在求 f [ i ] f[i] f[i]的时候别忘了 i i i之前没有比 A [ i ] A[i] A[i]小的数的情形,此时 A [ i ] A[i] A[i]自己一个数作为最长上升子序列,其长度和数量都是 1 1 1。其当固定了 i i i的时候,可以从 0 ∼ i − 1 0\sim i-1 0i1循环两遍求解,也可以只循环一遍:
1、我们用两个变量maxLenmaxCount分别表示能接在 A [ i ] A[i] A[i]之前的最长上升子序列的长度和其数量,初始化为 0 0 0
2、遍历 A [ 0 , . . . , i − 1 ] A[0,...,i-1] A[0,...,i1],判断 A [ ] < A [ i ] A[]<A[i] A[]<A[i]之后,如果发现 f [ ] [ 0 ] f[][0] f[][0]大于maxLen,说明发现了一个更长的上升子序列可以接到 A [ i ] A[i] A[i]后面,则更新maxLen f [ ] [ 0 ] f[][0] f[][0],并且更新maxCount f [ ] [ 1 ] f[][1] f[][1];如果发现 f [ ] [ 0 ] f[][0] f[][0]等于maxLen,说明又找到了一个长度最大的上升子序列,则让maxCount加上 f [ ] [ 1 ] f[][1] f[][1];如果发现 f [ ] [ 0 ] f[][0] f[][0]小于maxLen则不用理会,它不是已发现的最长的。
3、循环的同时可以用一个变量记录已经发现的最长上升子序列的长度,这样最后返回答案的时候,只需要返回 f [ ] [ 0 ] f[][0] f[][0]等于那个长度的对应的数量即可。

代码如下:

public class Solution {
    /**
     * @param nums: an array
     * @return: the number of longest increasing subsequence
     */
    public int findNumberOfLIS(int[] nums) {
        // Write your code here
        if (nums == null || nums.length == 0) {
            return 0;
        }
        
        // 这里的dp数组的含义就是上面f的含义
        int[][] dp = new int[nums.length][2];
        dp[0][0] = dp[0][1] = 1;
        
        // 初始化全局最长上升子序列的长度为1
        int globalMaxLen = 1;
        for (int i = 1; i < nums.length; i++) {
            int maxLen = 0, maxCount = 0;
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i]) {
                    if (dp[j][0] > maxLen) {
                        maxLen = dp[j][0];
                        maxCount = dp[j][1];
                    } else if (dp[j][0] == maxLen) {
                        maxCount += dp[j][1];
                    }
                }
            }
            
            dp[i][0] = maxLen + 1;
            // 如果i之前没有发现比A[i]小的数,则A[i]自成一个上升子序列,其数量是1
            dp[i][1] = Math.max(1, maxCount);
            
            globalMaxLen = Math.max(globalMaxLen, dp[i][0]);
        }
        
        int res = 0;
        for (int i = 0; i < dp.length; i++) {
            if (dp[i][0] == globalMaxLen) {
                res += dp[i][1];
            }
        }
        
        return res;
    }
}

时间复杂度 O ( n 2 ) O(n^2) O(n2),空间 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值