2970、2972、统计移除递增子数组的数目

  1. 题目
    1. 示例 1:
    2. 示例 2:
    3. 示例 3:
  2. 分析
  3. 数据量增大

题目

给你一个下标从 0 开始的 正 整数数组 nums 。

如果 nums 的一个子数组满足:移除这个子数组后剩余元素 严格递增 ,那么我们称这个子数组为 移除递增 子数组。比方说,[5, 3, 4, 6, 7] 中的 [3, 4] 是一个移除递增子数组,因为移除该子数组后,[5, 3, 4, 6, 7] 变为 [5, 6, 7] ,是严格递增的。

请你返回 nums 中 移除递增 子数组的总数目。

注意 ,剩余元素为空的数组也视为是递增的。

子数组 指的是一个数组中一段连续的元素序列。

示例 1:

输入:nums = [1,2,3,4]
输出:10
解释:10 个移除递增子数组分别为:[1], [2], [3], [4], [1,2], [2,3], [3,4], [1,2,3], [2,3,4] 和 [1,2,3,4]。移除任意一个子数组后,剩余元素都是递增的。注意,空数组不是移除递增子数组。

示例 2:

输入:nums = [6,5,7,8]
输出:7
解释:7 个移除递增子数组分别为:[5], [6], [5,7], [6,5], [5,7,8], [6,5,7] 和 [6,5,7,8] 。
nums 中只有这 7 个移除递增子数组。

示例 3:

输入:nums = [8,7,6,6]
输出:3
解释:3 个移除递增子数组分别为:[8,7,6], [7,6,6] 和 [8,7,6,6] 。注意 [8,7] 不是移除递增子数组因为移除 [8,7] 后 nums 变为 [6,6] ,它不是严格递增的。

提示:

  • 1 <= nums.length <= 50
  • 1 <= nums[i] <= 50

分析

如果这个数组本身的单调递增的,那么移除递增子数组的数量等组合数求和:

$$
\sum_n^i C_n^i = \sum_n^i i = n (n-1)/2
$$

如果数组中存在局部递增的情况:

  • 一个局部递增的情况,前缀递增,后缀递增
  • 多个局部递增的情况,需要移除多个非递增区域

任意一个数组的移除递增子数组的个数范围 [3,n(n-1)/2]

算法描述:

  • 统计前缀递增子数组的长度
  • 如果长度等于n直接返回结果
  • 否则,从后往前遍历:
    • 依次与前缀的递增子数组进行匹配,保证后缀递增和前缀递增进行拼接
    • 更新移除递增子序列的个数
class Solution {
    public int incremovableSubarrayCount(int[] nums) {
        int i = 0, n = nums.length;
        // 处理是否是全量递增数组
        while (i + 1 < n && nums[i] < nums[i + 1]) {
            ++i;
        }
        if (i == n - 1) {
            return n * (n + 1) / 2;
        }

        // 对于非全量递增数组,i标记了递减位置,从后往前遍历j in n-1 -> 0,找到第一个小于j位置元素的i
        int ans = i + 2;
        for (int j = n - 1; j > 0; --j) {
            // 找到第一个小于nums[j]的i的值
            while (i >= 0 && nums[i] >= nums[j]) {
                --i;
            }
            // 更新结果
            ans += i + 2;
            // 后缀非递增,直接退出循环
            if (nums[j - 1] >= nums[j]) {
                break;
            }
        }
        return ans;
    }
}

数据量增大

  • $1 <= nums.length <= 10^5$
  • $1 <= nums[i] <= 10^9$

返回值改为 long即可

class Solution {
    public long incremovableSubarrayCount(int[] a) {
        int n = a.length;
        int i = 0;
        while (i < n - 1 && a[i] < a[i + 1]) {
            i++;
        }
        if (i == n - 1) { // 每个非空子数组都可以移除
            return (long) n * (n + 1) / 2;
        }

        long ans = i + 2; // 不保留后缀的情况,一共 i+2 个
        // 枚举保留的后缀为 a[j:]
        for (int j = n - 1; j == n - 1 || a[j] < a[j + 1]; j--) {
            while (i >= 0 && a[i] >= a[j]) {
                i--;
            }
            // 可以保留前缀 a[:i+1], a[:i], ..., a[:0] 一共 i+2 个
            ans += i + 2;
        }
        return ans;
    }
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com

💰

Title:2970、2972、统计移除递增子数组的数目

Count:931

Author:攀登

Created At:2024-07-10, 19:09:33

Updated At:2024-07-10, 19:38:17

Url:http://jiafeimao-gjf.github.io/2024/07/10/2970-2972-%E7%BB%9F%E8%AE%A1%E7%A7%BB%E9%99%A4%E9%80%92%E5%A2%9E%E5%AD%90%E6%95%B0%E7%BB%84%E7%9A%84%E6%95%B0%E7%9B%AE/

Copyright: 'Attribution-non-commercial-shared in the same way 4.0' Reprint please keep the original link and author.

×

Help us with donation