统计移除递增子数组的数目II

2024-07-11

题目:

给你一个下标从 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 个移除递增子数组。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109

思路:

删除一个子数组后,整个数组还剩下前后两部分,需要满足这两个部分都是严格递增,并且第一部分的最后一个元素小于第二个部分的第一个元素。

基于这个思路,可以采用双指针的思想,先用左指针从前开始扫,指针停在最长严格递增的位置 l。如果 l 等于最后一个元素,表示删除任意一个子数组即可,此时可直接计算答案。可以删除 n 个长度为 1 的,n − 1 个长度为 2 的 ... 1 个长度为 n 的子数组,一共有 n * (n + 1) / 2 个子数组。

对于一般情况把右指针从后扫,当 nums[r] <= nums[l] 时,此时把左指针 l 向前移动直到满足 nums[r] > nums[l] 即可。此时可以删除子数组 [l, r − 1],[l − 1, r − 1] ... [0,  r − 1],一共可贡献 l + 2 个答案。计算完 r 的答案后,继续向前移动右指针,当不满足 nums[r] < nums[r+1] 时,停止遍历。

代码:

class Solution {
public:
    long long incremovableSubarrayCount(vector<int>& nums) {
        long long ans = 0;
        int len = nums.size();
        int l = 0;
        while (l < len - 1) {
            if (nums[l] >= nums[l + 1]) {
                break;
            }
            l++;
        }
        if (l == len - 1) {
            return 1LL * len * (len + 1) / 2;
        }

        ans += l + 2;
        for (int r = len - 1; r > 0; r--) {
            if (r < len - 1 && nums[r] >= nums[r + 1]) {
                break;
            }
            
            while (l >= 0 && nums[l] >= nums[r]) {
                l--;
            }
            ans += l + 2;
        } 
        
        return ans;
    }
};