📅  最后修改于: 2023-12-03 15:10:36.571000             🧑  作者: Mango
给定一个长度为 $n$ 的数组 $a$,请你计算最少需要划分几个子序列,使得每个子序列都是 $a$ 中的严格递增子序列。
首先,我们需要明确的一点是,每个递增子序列中的元素不能跳着取,只能是连续的一段。
接下来,我们可以使用贪心的思想来解决该问题。假设当前访问到了 $a_i$,如果 $a_i$ 比之前所有访问到的元素都要大,那么 $a_i$ 肯定应该加入到之前访问到的某个子序列中。但如果 $a_i$ 比之前某个元素小,那么我们就需要新开一个子序列了。
具体实现过程中,我们可以使用一个数组 $tail$,其下标 $i$ 表示当前使用长度为 $i$ 的递增子序列的最后一个元素的最小可能取值(也就是说,$tail_i$ 中存储了所有长度为 $i$ 的递增子序列的最后一个元素的最小取值)。比如,当 $tail$ 数组为 $[0, 0, 0, 0]$ 时,它表示目前使用长度为 $1$ 的递增子序列的最后一个元素的最小可能取值为 $0$。
接下来我们对数组 $a$ 进行遍历,对于每个 $a_i$,我们在 $tail$ 数组中找到一个最小的下标 $j$($1 \le j \le n$),使得 $tail_j < a_i$。此时 $a_i$ 肯定应该加入到长度为 $j$ 的递增子序列中,从而更新 $tail_j = a_i$。如果 $tail_j \ge a_i$,那么说明当前需要新开一个子序列,此时我们把 $a_i$ 加入到一个新的长度为 $j+1$ 的递增子序列中(因为此时已经没有长度为 $j$ 的递增子序列了),更新 $tail_{j+1} = a_i$。
经过上述操作后,$tail$ 数组中存储的就是所有递增子序列的最后一个元素中的最小值,它的长度就是所有子序列的个数。
def min_increasing_subsequence_count(a: List[int]) -> int:
n = len(a)
tail = [0] * (n+1)
len_of_subs = 0 # 当前递增子序列的长度
for i in range(n):
j = len_of_subs
while j > 0 and tail[j] >= a[i]:
j -= 1
if j == len_of_subs:
len_of_subs += 1
tail[j+1] = a[i]
return len_of_subs
时间复杂度:遍历一次数组 $a$,每次操作可以看作是对长度为 $n$ 的递增序列的二分查找,所以时间复杂度是 $O(n \log n)$。
空间复杂度:额外使用了一个数组 $tail$,所以空间复杂度是 $O(n)$。
本题相比于套装1所讲的问题增加了一些难度,但使用上述的思路,也可以用贪心的方式解决。