📅  最后修改于: 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)$。