题目
给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:
子数组大小 至少为 2 ,且
子数组元素总和为 k 的倍数。
如果存在,返回 true ;否则,返回 false 。
如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。0 始终视为 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
提示:
1 <= nums.length <= 105
0 <= nums[i] <= 109
0 <= sum(nums[i]) <= 231 - 1
1 <= k <= 231 - 1
思路和代码
这题我用了回溯用了暴力用了哈希和前缀和,都没有通过,我怎么那么优秀啊。
首先暴力很好想,就是枚举所有长度大于2的连续子数组,在判断一下是否满足条件,这很好写啊,超时。
回溯其实本质也就是暴力,但对于连续子数组的回溯(现在不会)我没写出来,写的有点问题,只能从第一个数开始试探情况。
最后看到了题目提示的关键词哈希表和前缀和,这我熟啊。但是不知道具体怎么用,就算求得到前缀和,那怎么得到不同连续子数组的情况,不还是要两层for循环遍历么?好的,吭哧吭哧写出来,调调调,漂亮,还是超时。
放弃,看别人的题解,太优秀了。
首先,这里面有一个一个概念——同余定理:
当两个数除以某个数的余数相等,那么二者相减后肯定可以被该数整除。
所以,哈希表怎么用?key是前缀和对k的余数,value是当前值的索引。(这谁啊,怎么那么聪明啊)
总体思路就是遍历数组,先求前缀和,然后求余res,判断余数是不是已经在字典dic中,且dic[res]和当前的索引i相差大于等于2,是则返回True。否则,注意,要判断余数在不在dic中,不在的话dic[res]=i。
为什么要多判断在不在,而不是直接赋值?
因为有种情况如[5,0,0,0],k=3。如果直接赋值,dic[2]前后等于0,1,2,本来后面三个0满足条件的,但这样一写,结果就会返回False。
此外,还有一种情况就是当前前缀和对k取余等于0,可以在for循环的时候判断一下,也可以在一开始的时候加入0:-1。
代码:
class Solution:def checkSubarraySum(self, nums: List[int], k: int) -> bool:# 前缀和 # 哈希表中 key是余数 value是下标# 对nums进行遍历,一边求前缀和,一边求余数# 判断余数是否已经在dic中 在的话 比较当前索引和dic[余数]相差是否大于等于2# 逻辑漏洞5,0,0,0 k=3 5,5,5,5 2对应的value一直在变 结果返回falsedic = {0:-1}pre = 0for i in range(len(nums)):pre += nums[i]res = pre % k# 前i个数正好整除k 要么在这写一个判断句 要么在初始时加0:-1# if res == 0:# return Trueif res in dic and i - dic[res] >=2:return True#加入这样的一个判断句 如果2已经存在 不改变其值if res not in dic:dic[res] = ireturn False