题目
给你一个整数数组 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 <= 10^5
0 <= nums[i] <= 10^9
0 <= sum(nums[i]) <= 2^31 - 1
1 <= k <= 2^31 - 1来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/continuous-subarray-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
数据规模是105,首先排除n2以上的算法。
要计算子数组和,一般可以考虑前缀和,对于区间[i,j],子数组和可以通过presum[j]-presum[i-1]求得,其中presum为nums的前缀和数组。
如果presum[j]-presum[i-1]是k的整数倍,那么(presum[j]-presum[i-1])%k0,可进一步得到presum[j]%kpresum[i-1]%k。
因此,遍历输入数组,一边计算前缀和,一边保存presum%k,遍历到j时,如果存在presum[i]%k和presum[j]%k相等,那么就说明找到了这样的子数组。
注意,题目要求长度至少为2,因此保存presum%k的同时,也同时保存下标,方便计算区间长度。
代码
class Solution {
public boolean checkSubarraySum(int[] nums, int k) {
Map<Long, Integer> map = new HashMap<>();
map.put(0L, -1);
long sum = 0;
int n = nums.length;
for (int i = 0; i < n; i++) {
sum += nums[i];
if (map.containsKey(sum % k)) {
if (i - map.get(sum % k) >= 2) {
return true;
}
} else {
// 这里要注意,只有map中不存在sum % k才put,是贪心的思想,这样可以使区间尽可能长,不会错过可能存在的解
// 如果每次都put,像[5,0,0,0] 3 这样的输入就会出错
map.put(sum % k, i);
}
}
return false;
}
}