673. 最长递增子序列的个数

        673. 最长递增子序列的个数

        这道题目是300. 最长递增子序列的进阶版。

        解决代码如下:

from bisect import bisect_left
class Solution(object):
    def findNumberOfLIS(self, nums):
        dp = [nums[0]]
        dp1 = [{nums[0] : 1}]
        for i in nums[1 : ]: 
            if i > dp[-1]:
                dp.append(i)
                dp1.append({i : 0})
                for key,value in dp1[-2].items():
                    if i > key:
                        dp1[-1][i] += value
            else:
                pos = bisect_left(dp, i)
                dp[pos] = i
                if i not in dp1[pos]:
                    dp1[pos][i] = 0
                if pos == 0:
                    dp1[0][i] += 1
                else:
                    for key, value in dp1[pos - 1].items():
                        if i > key:
                            dp1[pos][i] += value
        res = 0
        for value in dp1[-1].values():
            res += value
        return res

原理:

        维护dp和dp1数组

        dp数组索引 i 处记录的是当前构成长度为 i+1 的递增子序列的最小末尾数

        dp1和dp很类似,但是dp1索引 i记录的是应该字典,字典中记录的是当前所有长度为 i+1 的子序列,当它们末尾数为key时,有value种构成的方式(key必须构成尽量长的子序列,例如以7为末尾数时,最长能构成长度为3的子序列)

        对nums数组遍历

        比如 nums = [10,9,2,5,3,7,21,18]

        当遍历到nums[6] = 21时:

        因为构成长度为1的子序列有[10],[9],[2],[5],[3],[7],最小的末尾数是2

        构成长度为2的子序列有[2,5],[2,3],[2,7],[3,7],[5,7],最小的末尾数是3

        构成长度为3的子序列有[2,5,7]和[2,3,7],最小的末尾数是7

        所以此时dp = [2,3,7]

        因为dp是一个单调递增的数组,当遍历到nums[n](记为num)时,如果num > dp[-1],就在dp末尾插入num,否则,用bisect模块中的bisect_left函数,定位num应该在dp中插入的位置pos,然后更新

        注意不是把num插入到dp[pos]位置,而是将dp[pos]更新为num

        例如当遍历到nums[7] = 18时,此时dp = [2,3,7,21],定位18在dp中的位置,pos = 3,所以dp[3] = 18,dp = [2,3,7,18]

        接下来是如何维护dp1

        当遍历到nums[5] = 7(记为num)时:

        此时dp1 = [{10:1, 9:1, 2:1}, {5:1, 3:1}]

        由pos可知,当num为7时,最长的子序列长度变成了3,所以要在dp1末尾插入一个新的字典,这个字典新增key = 7,value = 当前长度为2的子序列,且末尾数小于7(这里有2和5)的key所对应的value之和 1+1=2 

        同理当遍历到nums[7] = 18 时:

        此时dp1 = [{10:1, 9:1, 2:1}, {5:1, 3:1}, {7:2}, {21:2}]

        由pos可知,18应该作为key添加到子序列长度为4的字典里面,即dp1[3],value等于当前子序列长度为3,且末尾数小于18(只有dp1[3][7])的key所对应的value之和 2

        当遍历完nums数组以后,dp1[-1]中的全部value加起来,就是所要求的最长递增子序列的个数

     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,我们需要定义一个数组dp,其中dp[i]表示以A[i]为结尾的最长递增子序列长度。 接下来,我们需要一个变量len来记录当前最长递增子序列长度,并且初始化为1。同时,我们还需要一个变量tail,表示目前已经构建的最长递增子序列的最后一个元素。 然后,我们从第二个元素开始遍历整个序列A。对于每个元素A[i],我们需要找到在它之前的所有元素中,比它小的元素中最长的递增子序列,然后把A[i]接到这个子序列后面。这样,我们就可以得到以A[i]为结尾的最长递增子序列。 具体来说,我们可以使用二分查找来找到在A[1...i-1]中比A[i]小的元素中最长的递增子序列。在查找过程中,我们需要维护一个tails数组,其中tails[k]表示长度为k的递增子序列的最后一个元素的最小值。对于每个A[i],我们都要在tails数组中找到第一个大于等于它的元素,然后用它来更新dp[i]和tails数组。 最终,最长递增子序列长度就是dp数组中的最大值。 下面是具体的算法实现: ``` def findLongestIncreasingSubsequence(A): n = len(A) dp = [1] * n tails = [A[0]] for i in range(1, n): if A[i] < tails[0]: tails[0] = A[i] elif A[i] > tails[-1]: tails.append(A[i]) else: l, r = 0, len(tails) - 1 while l < r: mid = (l + r) // 2 if tails[mid] < A[i]: l = mid + 1 else: r = mid tails[l] = A[i] dp[i] = len(tails) return max(dp) ``` 这个算法的时间复杂度是O(n log n),其中n是序列A的长度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值