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;
};

 

posted @ 2020-12-11 22:05  牛有肉  阅读(195)  评论(0编辑  收藏  举报