leetcode
leetcode 1351 ~ 1400
找两个和为目标值且不重叠的子数组

找两个和为目标值且不重叠的子数组

难度:

标签:

题目描述

代码结果

运行时间: 134 ms, 内存: 29.4 MB


/*
 * Given an integer array arr and an integer target,
 * find two non-overlapping subarrays such that their sums are equal to target.
 * Return the minimum sum of their lengths. If no such subarrays exist, return -1.
 * Java Stream API is not well suited for this type of problem due to its complex state management.
 * This implementation is provided for completeness but uses traditional loops internally.
 */
public class Solution {
    public int minSumOfLengths(int[] arr, int target) {
        int n = arr.length;
        int[] prefixMin = new int[n];
        Arrays.fill(prefixMin, Integer.MAX_VALUE);
        int sum = 0;
        int minLen = Integer.MAX_VALUE;
        int result = Integer.MAX_VALUE;
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, -1);
        IntStream.range(0, n).forEach(i -> {
            sum += arr[i];
            if (map.containsKey(sum - target)) {
                int start = map.get(sum - target) + 1;
                int len = i - start + 1;
                if (start > 0 && prefixMin[start - 1] != Integer.MAX_VALUE) {
                    result = Math.min(result, len + prefixMin[start - 1]);
                }
                minLen = Math.min(minLen, len);
            }
            prefixMin[i] = minLen;
            map.put(sum, i);
        });
        return result == Integer.MAX_VALUE ? -1 : result;
    }
}

解释

方法:

此题解采用了双指针和滑动窗口的方法来找到和为target的子数组。利用left和right指针控制窗口的左右边界。当窗口内元素之和小于target时右移right扩大窗口;当窗口内之和大于target时,右移left缩小窗口。每次窗口内之和等于target时,检查是否存在之前记录的非重叠子数组,如果存在,则更新可能的最小长度。为了记录和跟踪非重叠的子数组,使用deque(双端队列)来存储每个有效的子数组的起始和终止位置,并维护一个最小长度变量。当发现更短的子数组时,更新此最小值。最终,如果有符合条件的两个子数组,返回它们长度之和的最小值;否则返回-1。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在算法中,如何确保找到的两个子数组确实是互不重叠的?
在算法中,通过使用双端队列deque来存储满足条件的子数组的起止位置,确保找到的两个子数组互不重叠。当找到一个新的和为target的子数组时,算法会检查deque中的子数组,如果队列中已有的子数组的结束位置小于或等于当前子数组的开始位置left,表明这两个子数组不重叠。这样,通过队列中存储的子数组的起止位置信息,可以有效地筛选并维护不重叠的子数组。
🦆
算法中使用的双端队列是如何帮助寻找最小长度和的?即,队列中存储的子数组信息如何被利用?
双端队列在算法中用于存储每个满足条件(和等于target)的子数组的起始和终止位置。这种存储方式方便了快速地添加新的子数组和移除旧的子数组。当发现新的满足条件的子数组时,算法会检查队列中是否有与当前子数组不重叠的子数组。如果有,算法会计算两个子数组长度之和并尝试更新最小长度和。此外,队列通过维护子数组的顺序,帮助快速地找到与当前子数组不重叠的最早的子数组,从而高效更新最小长度和。
🦆
为什么在发现窗口和等于目标值时,首先要检查并移除队列中所有结束位置小于等于当前左指针的子数组?
这样做是为了确保队列中的子数组与当前考察的子数组不重叠。当窗口和等于目标值时,需要评估这个窗口与队列中已有的子数组是否重叠。移除所有结束位置小于等于当前左指针的子数组是因为这些子数组的有效性已经过时(它们的范围不可能与当前或未来的窗口重叠),清除这些子数组有助于保持队列的有效性和减少不必要的计算,同时确保我们只比较不重叠的子数组,以有效更新最小长度和。
🦆
在更新非重叠子数组的最小长度`mn`时,为什么选择从队列中移除元素,而这些元素的结束位置小于等于当前的`left`指针?
移除结束位置小于等于当前left指针的子数组元素,是因为这些子数组已不可能与当前窗口或任何后续窗口形成不重叠的子数组组合。这种移除操作有助于维护一个干净且高效的队列状态,确保队列中只包含可能与当前或后续窗口组成有效组合的子数组。这样同时也简化了非重叠子数组最小长度mn的更新过程,因为我们只需考虑队列中剩余的有效子数组。

相关问题