Leetcode 1477 两个和为目标值且不重合的子数组 滑动窗口
所求子数组是连续的子数组,因此想到求取所有可能连续子数组的和。
顺序的求取连续子数组的和具有单调性,因此可以考虑通过滑动窗口来避免重复计算。
public final int minSumOfLengths(int[] arr, int target) { int startPoint = 0, endPoint = 0, sum = 0, re = Integer.MAX_VALUE; List<Long> nums = new LinkedList<Long>(); while (startPoint < arr.length && endPoint < arr.length) { sum += arr[endPoint]; if (sum < target) endPoint++; else { if (sum == target) { if (endPoint - startPoint == 2) return 2; for (int i = nums.size() - 1; i >= 0; i--) { int preEnd = (int) (long) nums.get(i); if (preEnd >= startPoint) continue; int preBegin = (int) (nums.get(i) >> 32); re = Math.min(re, preEnd - preBegin + endPoint - startPoint + 2); } nums.add(((long) startPoint << 32) | endPoint); endPoint++; } else sum -= arr[endPoint]; sum = startPoint == endPoint ? sum : sum - arr[startPoint]; startPoint++; if (endPoint < startPoint) endPoint = startPoint; } } return re == Integer.MAX_VALUE ? -1 : re; }
在寻找两个子数组的组合时,采用了遍历查找的方式,该解法超时。考虑采用更合理的数据结构进行符合要求的子数组的查找,这里采用二叉排序树:
public final int minSumOfLengths0(int[] arr, int target) { int startPoint = 0, endPoint = 0, sum = 0, re = Integer.MAX_VALUE; TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>(); while (startPoint < arr.length && endPoint < arr.length) { sum += arr[endPoint]; if (sum < target) endPoint++; else { if (sum == target) { SortedMap<Integer, Integer> pres = map.headMap(startPoint); Iterator<Integer> iterator = pres.keySet().iterator(); while (iterator.hasNext()) { int key = iterator.next(); re = Math.min(re, key - pres.get(key) + endPoint - startPoint + 2); } map.put(endPoint, startPoint); endPoint++; } else sum -= arr[endPoint]; sum = startPoint == endPoint ? sum : sum - arr[startPoint]; startPoint++; if (endPoint < startPoint) endPoint = startPoint; } } return re == Integer.MAX_VALUE ? -1 : re; }
依然超时,考虑使用动态规划优化查找。寻找最小的两个子数组的和很难找到合适的状态转移方程,但可以注意到,可以复用 “某个节点之前的和为 target 的最小子数组” 的结果:
public final int minSumOfLengths1(int[] arr, int target) { int sum = 0, len = arr.length, maxRe = Integer.MAX_VALUE / 2, re = maxRe; int[] dp = new int[len]; Arrays.fill(dp, maxRe); for (int i = 0, j = 0; j < len; j++) { sum += arr[j]; while (sum > target && i <= j) { sum -= arr[i]; i++; } if (sum == target) { int currentLen = j - i + 1; dp[j] = currentLen; if (i != 0) re = Math.min(re, dp[i - 1] + currentLen); } if (j > 0) dp[j] = Math.min(dp[j - 1], dp[j]); } return re == maxRe ? -1 : re; }
JS:
/** * @param {number[]} arr * @param {number} target * @return {number} */ var minSumOfLengths = function (arr, target) { let sum = 0, re = Number.MAX_VALUE, len = arr.length, dp = new Array(len); for (let i = 0, j = 0; j < len; j++) { sum += arr[j]; while (sum > target && i <= j) { sum -= arr[i]; i++; } if (sum == target) { dp[j] = j - i + 1; if (i != 0) re = Math.min(re, dp[i - 1] + dp[j]); } if (!dp[j]) dp[j] = Number.MAX_VALUE; if (j != 0) dp[j] = Math.min(dp[j - 1], dp[j]); console.log(dp[j]); } return re == Number.MAX_VALUE ? -1 : re; };
当你看清人们的真相,于是你知道了,你可以忍受孤独