📜  最长递增子序列的数量(1)

📅  最后修改于: 2023-12-03 15:40:17.481000             🧑  作者: Mango

最长递增子序列的数量

最长递增子序列(Longest Increasing Subsequence,简称LIS)是指在给定序列中找到一个子序列,使得这个子序列的元素在原序列中的相对顺序不变且递增,同时其长度最大。最长递增子序列的数量即为满足条件的子序列数量。

解法
动态规划

最长递增子序列可以用动态规划解决。设 $dp[i]$ 为以第 $i$ 个元素为结尾的最长递增子序列的长度,则有如下状态转移方程:

$$ dp[i] = \max_{0 \leq j < i}{(dp[j] + 1)} $$

其中,$dp[j]$ 表示以第 $j$ 个元素为结尾的最长递增子序列的长度。而最长递增子序列的数量则可以在求解 $dp$ 数组的过程中计算得到。

def LIS(nums):
    n = len(nums)
    lengths = [1] * n # dp数组
    counts = [1] * n # 子序列数量数组

    for i in range(1, n):
        for j in range(i):
            if nums[j] < nums[i]:
                if lengths[j] >= lengths[i]:
                    lengths[i] = lengths[j] + 1
                    counts[i] = counts[j]
                elif lengths[j] + 1 == lengths[i]:
                    counts[i] += counts[j]
    
    max_length = max(lengths)
    res = 0
    for i in range(n):
        if lengths[i] == max_length:
            res += counts[i]

    return res
二分查找

最长递增子序列也可以用二分查找优化到 $O(n\log n)$ 的时间复杂度。具体来说,维护一个递增的数组 $d$,其中 $d[i]$ 表示长度为 $i$ 的递增子序列的最后一个元素的最小值。遍历原序列,将每个元素二分插入到 $d$ 数组中,对于每个元素能够插入到的最长递增子序列的长度即为其插入到 $d$ 数组之后的下标。

import bisect

def LIS(nums):
    n = len(nums)
    d = []

    for num in nums:
        i = bisect.bisect_left(d, num)
        if i == len(d):
            d.append(num)
        else:
            d[i] = num
    
    return len(d)
总结

最长递增子序列的数量是一个子序列问题,可以通过动态规划或者二分查找等方式解决。其中,动态规划的时间复杂度为 $O(n^2)$,而二分查找的时间复杂度为 $O(n\log n)$。