前缀和是数据结构与算法中比较重要的知识,前缀和经常可以结合哈希表解决很多有意思的问题。为了方便学习,在这里总结leetcode中出现的前缀和问题。
525. 连续数组
给定一个二进制数组 nums (只含有0,1), 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。
示例 1:
输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量0和1的最长连续子数组。
示例 2:
输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
思路:我们用ans来统计数组中0,1出现的次数,即当出现0时,ans - 1,当出现1时,ans + 1。这样我们就可以知道index下出现了0和1出现的个数差。我们用dict来存储ans,key=index,value=ans。因为在统计ans之前,0,1个数差为0,所以初始化时dict = {0:-1}。当ans没有出现在dict时,我们将ans与index记录到dict中,当dict已经存在ans,那么说明这段子序列里0和1出现次数相同,我们计算出子序列的个数,即i - dict[ans]。然后遍历nums找到最长连续子数组的长度。为了方便理解请看下图。
class Solution:
def findMaxLength(self, nums: List[int]) -> int:
sum_dict = {0:-1}
ans = 0
res = 0
for i,num in enumerate(nums):
if num == 0:
ans -= 1
elif num == 1:
ans += 1
if ans not in sum_dict:
sum_dict[ans] = i
elif ans in sum_dict:
res = max(res, i - sum_dict[ans])
return res
523. 连续的子数组和
给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:
子数组大小 至少为 2 ,且
子数组元素总和为 k 的倍数。
如果存在,返回 true ;否则,返回 false 。
如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。
示例 1:
输入:nums = [23,2,4,6,7], k = 6
输出:true
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6 。
示例 2:
输入:nums = [23,2,6,4,7], k = 6
输出:true
解释:[23, 2, 6, 4, 7] 是大小为 5 的子数组,并且和为 42 。
42 是 6 的倍数,因为 42 = 7 * 6 且 7 是一个整数。
示例 3:
输入:nums = [23,2,6,4,7], k = 13
输出:false
思路:为了方便计算子数组的和,我们用presum[i]统计0-i索引下的数组和,任意i-j的数组和可以用presum[j] - presum[i]获得。根据同余定理,我们可以用dict存储presum % k的值,当遍历presum时,如果i - dict[presum[i] >=2就返回True
class Solution:
def checkSubarraySum(self, nums: List[int], k: int) -> bool:
presum = [0]*(len(nums) + 1)
for i in range(1,len(presum)):
presum[i] = presum[i-1] + nums[i-1]
presum[i] %= k
predict = {}
for i, re in enumerate(presum):
if re not in predict:
predict[re] = i
for i in range(2, len(presum)):
if i - predict[presum[i]] >= 2:
return True
return False