leetcode
leetcode 2551 ~ 2600
统计移除递增子数组的数目 I

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

难度:

标签:

题目描述

You are given a 0-indexed array of positive integers nums.

A subarray of nums is called incremovable if nums becomes strictly increasing on removing the subarray. For example, the subarray [3, 4] is an incremovable subarray of [5, 3, 4, 6, 7] because removing this subarray changes the array [5, 3, 4, 6, 7] to [5, 6, 7] which is strictly increasing.

Return the total number of incremovable subarrays of nums.

Note that an empty array is considered strictly increasing.

A subarray is a contiguous non-empty sequence of elements within an array.

 

Example 1:

Input: nums = [1,2,3,4]
Output: 10
Explanation: The 10 incremovable subarrays are: [1], [2], [3], [4], [1,2], [2,3], [3,4], [1,2,3], [2,3,4], and [1,2,3,4], because on removing any one of these subarrays nums becomes strictly increasing. Note that you cannot select an empty subarray.

Example 2:

Input: nums = [6,5,7,8]
Output: 7
Explanation: The 7 incremovable subarrays are: [5], [6], [5,7], [6,5], [5,7,8], [6,5,7] and [6,5,7,8].
It can be shown that there are only 7 incremovable subarrays in nums.

Example 3:

Input: nums = [8,7,6,6]
Output: 3
Explanation: The 3 incremovable subarrays are: [8,7,6], [7,6,6], and [8,7,6,6]. Note that [8,7] is not an incremovable subarray because after removing [8,7] nums becomes [6,6], which is sorted in ascending order but not strictly increasing.

 

Constraints:

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

代码结果

运行时间: 30 ms, 内存: 15.9 MB


/*
 * 思路:
 * 1. 使用Java Stream API来生成所有可能的子数组。
 * 2. 对每一个子数组,移除后检查剩余数组是否严格递增。
 * 3. 统计满足条件的子数组数量。
 */
import java.util.Arrays;
import java.util.stream.IntStream;

public class SolutionStream {
    public long countIncreasingSubarrays(int[] nums) {
        int n = nums.length;
        return IntStream.range(0, n)
                .boxed()
                .flatMap(i -> IntStream.range(i, n)
                        .mapToObj(j -> removeSubarray(nums, i, j)))
                .filter(this::isStrictlyIncreasing)
                .count();
    }

    private int[] removeSubarray(int[] nums, int start, int end) {
        return IntStream.range(0, nums.length)
                .filter(i -> i < start || i > end)
                .map(i -> nums[i])
                .toArray();
    }

    private boolean isStrictlyIncreasing(int[] nums) {
        return IntStream.range(1, nums.length)
                .allMatch(i -> nums[i] > nums[i - 1]);
    }
}

解释

方法:

该题解采用滑动窗口的方法来寻找所有可能的子数组,并检查每个子数组是否为移除递增子数组。具体做法是使用双指针技术,其中外层循环的指针left代表子数组的起始位置,内层循环的指针right代表子数组的结束位置。对于每一个由left和right定义的子数组,调用getAscFlag函数来检查除去这个子数组外,剩余的数组部分是否严格递增。如果是,则计数器ans增加1。最终,返回ans,即所有满足条件的子数组数量。

时间复杂度:

O(n^3)

空间复杂度:

O(1)

代码细节讲解

🦆
在函数`getAscFlag`中使用`float('-inf')`初始化变量`l`的原因是什么?
在函数`getAscFlag`中,变量`l`用于存储遍历到当前元素之前的最大值。使用`float('-inf')`初始化`l`是为了确保在比较数组第一个元素时,任何正整数都大于`-∞`。这样可以保证对数组第一个元素的正确处理,使得逻辑在对数组进行严格递增性检查时不会因为未初始化或错误的初始值而出错。
🦆
为什么整个算法选择使用双层循环遍历所有可能的子数组?这种方法在最坏情况下的时间复杂度是多少?
算法使用双层循环(两个嵌套的`for`循环),通过左指针`left`和右指针`right`来遍历数组`nums`中所有可能的子数组。这样做是为了确保每一个子数组都被考虑到,从而能够检查其是否为一个移除递增子数组。在最坏情况下,即当数组长度为`n`时,外层循环需要遍历`n`次,内层循环在不同的外层迭代中平均需要遍历`n/2`次。因此,总的操作数量是`n * (n + 1) / 2`,即`O(n^2)`。此外,每次内部循环迭代时都需要调用`getAscFlag`函数检查剩余数组是否严格递增,这需要额外的`O(n)`时间,因此整体时间复杂度为`O(n^3)`。
🦆
在检查剩余数组是否严格递增时,`getAscFlag`函数是否考虑了数组的边界情况,如子数组在数组的开始或结束位置?
函数`getAscFlag`确实考虑了数组的边界情况。该函数通过遍历整个数组`nums`并跳过`left`到`right`索引之间的元素来检查剩余部分是否严格递增。如果子数组位于数组的开始或结束,`left`和`right`指针会相应地调整,确保从数组的非子数组部分开始比较。例如,如果子数组在开头,从`right + 1`开始检查;如果在结尾,只需要检查到`left - 1`为止。这保证了无论子数组位于何处,`getAscFlag`都能正确地仅对剩余部分进行递增性检查。

相关问题