题目链接:https://leetcode-cn.com/problems/continuous-subarray-sum/
给你一个整数数组 nums
和一个整数 k
,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:
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
提示:
1 1 1 <= nums.length
<= 1 0 5 10^5 105
0 0 0 <= nums[i]
<= 1 0 9 10^9 109
0 0 0 <= sum(nums[i])
<= 2 31 − 1 2^{31} - 1 231−1
1 1 1 <= k
<= 2 31 − 1 2^{31} - 1 231−1
子数组和的获取:构建前缀和数组sums
,sums[j]-sums[i]
即为nums[i+1] + ... + nums[j]
所以:子数组元素总和为k的倍数 → sums[j]-sums[i] % k == 0
根据同余定理:如果两个整数m、n满足n-m能被k整除,那么n和m对k同余
则有:sums[j]-sums[i] % k == 0
→ sums[j] % k == sums[i] % k
于是问题转化为检查sums
中是否存在同余项
所以可以通过哈希表保存一个余数第一次出现的下标
而根据余数的性质:
(a0 + a1 + … + ai) % k = (a0 % k + a1 % k + … ai % k ) % k
前i
项和的余数可以仅由前i -1
项和和的余数得到:
rm = (rm + nums[i]) % k;
public boolean checkSubarraySum(int[] nums, int k) {
if (nums.length < 2) {
return false;
}
Map<Integer, Integer> m = new HashMap<>();
int rm = 0;
// 注意:当第一次出现余数为0的情况时说明0~i的总和已经满足条件
// 不必继续判断其他子数组和的情况是否满足
// 所以添加(0, -1)作辅助判断
m.put(0, -1);
for (int i = 0; i < nums.length; i++) {
rm = (rm + nums[i]) % k;
if (m.containsKey(rm)) {
int pre = m.get(rm);
if (i - pre >= 2) {
return true;
}
} else {
m.put(rm, i);
}
}
return false;
}