leetcode1546. 和为目标值的最大数目不重叠非空子数组数目(Python3、c++)

博客介绍了如何利用前缀和、哈希表和贪心策略解决LeetCode1546题,寻找和为目标值的不重叠子数组最大数量。通过举例和详细解释思路,阐述了当前缀和差值等于目标值时,存在符合条件的子数组,并使用哈希表在O(1)时间内查找。贪心方法确保子数组不重叠,提高了解决方案的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

给你一个数组 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

方法:前缀和+哈希表+贪心

思路:

和为某个值的子数组,应该使用前缀和的方法,**当两个前缀和相减值为target的时候,说明这两个位置中间的子数组即为符合条件的。**我们使用summ保存前缀和的话,如果summ-target前缀和出现过,那么就有一个符合条件的子数组。

这个查找过程需要是O(1)时间复杂度的,因此我们使用一个集合(哈希表)来保存出现过的前缀和summ。那么在继续计算前缀和summ的过程中,如果summ-target在集合中存在,那么则有一个符合条件的数组。

下面就要考虑如何使得子数组不重叠。我们使用贪心的方法,即找到一个满足条件的数组res++,然后清空集合,相当于把前面的都抛弃掉了,即前面满足条件的数组的这部分不会再参与,这样得到的子数组都是不重叠的。

下面验证为什么这种贪心的方法是有效的。假设我们有两个符合条件的重叠数组,nums为[1,2,1],target=3,那么符合条件的是[1,2]和[2,1],它们是重叠的,因此这个nums的答案只有1。我们在遍历时候,找到[1,2]的时候,就把前面抛弃掉了,因此只会找到一个答案,答案就是1。

这个方法相当于,如果有重叠的数组,我们只遍历到前面的这个子数组,然后把重叠的部分抛弃了,这样就不会再找到原来重叠的子数组了。我们就可以得到正确的答案了。

代码:

Python3:
class Solution:
    def maxNonOverlapping(self, nums: List[int], target: int) -> int:
        res = 0
        # 集合,保存出现过的前缀和
        pres = {0}
        # 前缀和
        summ = 0
        # 遍历
        for num in nums:
            # 计算前缀和
            summ += num
            # 如果summ-target存在于pres,那么res++,再清空集合
            if summ-target in pres:
                res += 1
                pres.clear()
            # 将更新的前缀和加入集合
            pres.add(summ)
        return res

cpp:
class Solution {
public:
    int maxNonOverlapping(vector<int>& nums, int target) {
        int res = 0;
        // 集合,保存出现过的前缀和
        set<int>pres;
        pres.insert(0);
        // 前缀和
        int summ = 0;
        // 遍历
        for (auto &num:nums){
            // 计算前缀和
            summ += num;
            // 如果summ-target存在于pres,那么res++,再清空集合
            if (pres.find(summ-target)!=pres.end()){
                res ++;
                pres.clear();
            }
            // 将更新的前缀和加入集合
            pres.insert(summ);
        }          
        return res;       
    }
};

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值