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

300. 最长递增子序列

方法一:动态规划

gjghjhgj_动态规划gjghjhgj_算法_02 分别表示以 gjghjhgj_python_03

状态转移方程为:

gjghjhgj_算法_04

对于 gjghjhgj_算法_02,其等于所有满足 gjghjhgj_数组_06gjghjhgj_python_07

class Solution:
    def findNumberOfLIS(self, nums: List[int]) -> int:
        n, max_len, ans = len(nums), 0, 0
        dp = [1] * n
        cnt = [1] * n
        for i, x in enumerate(nums):
            for j in range(i):
                if x > nums[j]:
                    if dp[j] + 1 > dp[i]:
                        dp[i] = dp[j] + 1
                        cnt[i] = cnt[j]  # 重置计数
                    elif dp[j] + 1 == dp[i]:
                        cnt[i] += cnt[j]
            if dp[i] > max_len:
                max_len = dp[i]
                ans = cnt[i]  # 重置计数
            elif dp[i] == max_len:
                ans += cnt[i]
                
        return ans

方法二:贪心 + 前缀和 + 二分查找

将数组 d 扩展成一个二维数组,其中 d[i] 数组表示所有能成为长度为 i 的最长上升子序列的末尾元素的值。具体地,将更新 gjghjhgj_动态规划_08 这一操作替换成将 gjghjhgj_数组_09 置于 gjghjhgj_python_10 数组末尾。这样 gjghjhgj_python_10 中就保留了历史信息,且 gjghjhgj_python_10

类似地,也可以定义一个二维数组 gjghjhgj_python_13,其中 gjghjhgj_动态规划_14 记录了以 gjghjhgj_算法_15 为结尾的最长上升子序列的个数。为了计算 gjghjhgj_动态规划_14,可以考察 gjghjhgj_算法_17gjghjhgj_子序列_18,将所有满足 gjghjhgj_子序列_19gjghjhgj_python_20 累加到 gjghjhgj_动态规划_14,这样最终答案就是 gjghjhgj_数组_22

在代码实现时,由于 gjghjhgj_python_10 中的元素是有序的,可以二分得到最小的满足 gjghjhgj_子序列_19 的下标 k。另一处优化是将 gjghjhgj_python_13 改为其前缀和,并在开头填上 0,此时 gjghjhgj_算法_15 对应的最长上升子序列的个数就是 gjghjhgj_数组_27,这里 gjghjhgj_算法_28

class Solution:
    def findNumberOfLIS(self, nums: List[int]) -> int:
        d, cnt = [], []
        for v in nums:
            i = bisect(len(d), lambda i: d[i][-1] >= v)
            c = 1
            if i > 0:
                k = bisect(len(d[i - 1]), lambda k: d[i - 1][k] < v)
                c = cnt[i - 1][-1] - cnt[i - 1][k]
            if i == len(d):
                d.append([v])
                cnt.append([0, c])
            else:
                d[i].append(v)
                cnt[i].append(cnt[i][-1] + c)
        return cnt[-1][-1]

def bisect(n: int, f: Callable[[int], bool]) -> int:
    l, r = 0, n
    while l < r:
        mid = (l + r) // 2
        if f(mid):
            r = mid
        else:
            l = mid + 1
    return l