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