leetcode
leetcode 2451 ~ 2500
无限数组的最短子数组

无限数组的最短子数组

难度:

标签:

题目描述

You are given a 0-indexed array nums and an integer target.

A 0-indexed array infinite_nums is generated by infinitely appending the elements of nums to itself.

Return the length of the shortest subarray of the array infinite_nums with a sum equal to target. If there is no such subarray return -1.

 

Example 1:

Input: nums = [1,2,3], target = 5
Output: 2
Explanation: In this example infinite_nums = [1,2,3,1,2,3,1,2,...].
The subarray in the range [1,2], has the sum equal to target = 5 and length = 2.
It can be proven that 2 is the shortest length of a subarray with sum equal to target = 5.

Example 2:

Input: nums = [1,1,1,2,3], target = 4
Output: 2
Explanation: In this example infinite_nums = [1,1,1,2,3,1,1,1,2,3,1,1,...].
The subarray in the range [4,5], has the sum equal to target = 4 and length = 2.
It can be proven that 2 is the shortest length of a subarray with sum equal to target = 4.

Example 3:

Input: nums = [2,4,6,8], target = 3
Output: -1
Explanation: In this example infinite_nums = [2,4,6,8,2,4,6,8,...].
It can be proven that there is no subarray with sum equal to target = 3.

 

Constraints:

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

代码结果

运行时间: 87 ms, 内存: 25.2 MB


/*
 * 思路:使用Java Stream API的部分功能,例如IntStream生成重复序列,
 * 但由于滑动窗口和更新最小长度的操作,stream并不完全合适。
 * 为了模拟无限数组,我们在滑动窗口中直接操作原数组。
 */
import java.util.stream.IntStream;

public class StreamSolution {
    public int minSubarrayLength(int[] nums, int target) {
        int n = nums.length;
        int minLength = Integer.MAX_VALUE;
        int currentSum = 0;
        int left = 0;

        for (int right = 0; right < 2 * n; right++) {
            currentSum += nums[right % n];

            while (currentSum >= target) {
                minLength = Math.min(minLength, right - left + 1);
                currentSum -= nums[left % n];
                left++;
            }
        }

        return minLength == Integer.MAX_VALUE ? -1 : minLength;
    }
}

解释

方法:

此题解首先考虑了nums数组和target的关系。通过滑动窗口方法寻找一个或多个连续子数组的和等于给定target的最短长度。主要分为以下几步:1. 计算nums数组的总和total。2. 根据target与total的比较,决定是否可以通过简单的除法运算直接得出结果。3. 使用辅助函数mx_sub来找到和为特定值的最短或最长子数组。4. 处理target分成total的整数倍和余数的情况,通过适当的调整和组合,尝试找到符合条件的最短子数组。代码中使用了两次mx_sub函数,分别处理求最短子数组和最长子数组的情况。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在解法中,为什么首先检查`total`(nums数组的总和)和`target`的关系?这样的比较有什么特殊的意义吗?
这种比较有助于快速确定解决方案的方向或简化问题。如果`total`小于`target`,那么不可能有任何子数组的和等于`target`,因此可以直接返回无解。如果`total`恰等于`target`,则整个数组就是所需的子数组,也就无需进一步查找。最后,如果`total`大于`target`,则可能存在一个或多个子数组的和等于`target`,这时候需要通过滑动窗口等方法进一步寻找。
🦆
函数`mx_sub`中参数`flag`的作用是什么?如何根据这个参数改变函数的行为?
`flag`参数在`mx_sub`函数中用以区分查找最长或最短满足条件的子数组。当`flag`为True时,函数寻找和为`t`的最长子数组;当`flag`为False时,寻找和为`t`的最短子数组。这通过在找到和为`t`的子数组时,使用`max`或`min`函数来更新结果来实现,即保留历史中最大或最小的子数组长度。
🦆
代码实现了`mx_sub`以处理最短和最长子数组的查找,但如何确保`mx_sub`函数能够正确处理跨越原始`nums`数组边界的子数组情况?
为了处理可能跨越数组边界的子数组,题解中采用了将`nums`数组自身与其副本拼接的方法(`nums + nums`),从而模拟一个循环数组的环境。这样,即使子数组从原数组的末尾开始并延续到开头,也可以被视为数组中连续的部分,从而正确地计算其长度。
🦆
在处理`target`分成`total`的整数倍和余数的情况时,为什么选择`target = total - rest`而不是直接使用`rest`?这种方法的逻辑基础是什么?
选择`target = total - rest`而不是`rest`的原因在于,这样可以转换为查找数组中和为`total - rest`的最长子数组,然后用整个数组的长度减去这个最长子数组的长度,得到和为`rest`的最短子数组的长度。这种转换是基于数学关系和问题转化的思想,将原问题转换为更容易计算和理解的形式。

相关问题