来自leetcode 560
前言
自己只会暴力,这里就是记录一下前缀和+哈希表的做法,来自灵神的前缀和+哈希表:从两次遍历到一次遍历,附变形题
正文
首先,这道题无法使用滑动窗口,因为滑动窗口需要满足单调性,当右端点元素进入窗口时,窗口元素和是不能减少的。但是这道题中数组元素可以是负数,因此元素和可能会减少,不满足单调性,所以无法使用滑动窗口。
暴力
比较容易想到的当然是暴力。两个for循环,把所有的子数组的和都计算一遍,并判断和是否为k。暴力直接超时。
前缀和+哈希表
这里,灵神先给了两数之和的优化思路【动画】从两数之和中,我们可以学到什么?,感觉对于理解这道题很有用。
设 i<j,如果 nums[i] 到 nums[j−1] 的元素和等于 k,用前缀和表示,就是
s[j]−s[i]=k。
枚举 j,上式变成s[i]=s[j]−k。
对于问题【该数组中和为k的子数组的个数】,就可以变相理解为:
对于每个 s[j],分别计算有多少个 s[i] 满足 i<j 且 s[i]=s[j]−k(已知 s[j] 和 k,统计 s[0] 到 s[j−1] 中有多少个数等于 s[j]−k),然后再将每个 s[j]计算得到的 s[i] 的个数相加就得到最终答案。
比如有例子:nums=[1,1,−1,1,−1], k=1,其前缀和 s=[0,1,2,1,2,1]。那么流程如下图,对于每个s[j]判断前面有几个s[i]满足s[i]=s[j]-k。
而哈希表 cnt 的作用就是记录已经遍历过的s元素的个数,那么枚举到 s[j] 时,从哈希表中就可以直接通过 cnt[s[j]−k]找到有几个 s[i]。因此每次枚举到s[j]后就把s[j]加入到哈希表中或更新s[j]的个数。
代码如下,需要两次遍历,一次是构建前缀和,一次是遍历前缀和:
以下代码只需要一次遍历,一边构建前缀和一边遍历: