LeetCode Everyday - 和为目标值且不重叠的非空子数组的最大数目

111 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情 >>

和为目标值且不重叠的非空子数组的最大数目

给你一个数组 nums 和一个整数 target 。

请你返回 非空不重叠 子数组的最大数目,且每个子数组中数字和都为 target 。

示例1:

输入:nums = [1,1,1,1,1], target = 2
输出:2
解释:总共有 2 个不重叠子数组(加粗数字表示) [1,1,1,1,1] ,它们的和为目标值 2

示例2:

输入:nums = [-1,3,5,1,4,2,-9], target = 6
输出:2
解释:总共有 3 个子数组和为 6 。
([5,1], [4,2], [3,5,1,4,2,-9]) 但只有前 2 个是不重叠的。

示例3:

输入: nums = [-2,6,6,3,5,4,1,2,8], target = 10
输出: 3

示例4:

输入: nums = [0,0,0], target = 0
输出: 3

提示:

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
  • 0 <= target <= 10^6

解题思路:

设数组nums的长度为len
dp[i]代表从数组nums的下标0到i的子数组最大不重叠子数组的数目
状态转移方程
当nums[i]作为一个子数组sub时,能得到的最大值dp[i-1] + (如果sub累加和为target则+1,否则+0)
当nums[i]和nums[i-1]作为一个子数组sub时,能得到的最大值dp[i-2] + (如果sub累加和为target则+1,否则+0)
以此类推
dp[i] = max{dp[i-1] + (sum{nums[i]} == target ? 1 : 0) , dp[i-2] + (sum{nums[i], nums[i + 1]} == target ? 1 : 0), .... , (sum{nums[i], nums[i + 1], ...., nums[len - 1]} == target ? 1 : 0)}
其中求和可以通过前缀和的方法的在O(1)的时间内求出
所以求解每个dp[i]都需要遍历dp[i-1], dp[i-2], dp[i-3]到结尾,时间复杂度O(n^2)

我的答案:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var maxNonOverlapping = function(nums, target) {
    const len = nums.length
    const map = new Map()
    const dp = Array(len).fill(0)
    let preSum = 0
    map.set(preSum, -1)
    for(let i = 0; i < len; i++){
        preSum += nums[i]
        let p1 = i - 1 >= 0 ? dp[i - 1] : 0
        const index = map.get(preSum - target)
        let p2 = 0
        if(index != undefined){
            p2 = 1 + (index >= 0 ? dp[index] : 0)
        }else{
            p2 = nums[i] == target ? 1 : 0
        }
        dp[i] = Math.max(p1, p2)
        map.set(preSum, i)
    }
    return dp[len - 1]
};

最后

如果有更好的解法或者思路, 欢迎在评论区和我交流~ ღ( ´・ᴗ・` )