时间
2021年6月2日
题目
给你一个整数数组 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
提示:
1 <= nums.length <= 105
0 <= nums[i] <= 109
0 <= sum(nums[i]) <= 231 - 1
1 <= k <= 231 - 1
解答
第一次解答(超时,O(n^2))
class Solution {
public boolean checkSubarraySum(int[] nums, int k) {
int n = nums.length;
int j = 0;
for (int i = 1; i < n; i++) {
int sum = nums[i-1] + nums[i];
j = i + 1;
while (sum % k != 0 && j < n) {
sum += nums[j++];
}
if(sum % k == 0){
return true;
}
}
return false;
}
}
思路
思路还是没有问题的,只是超时了。通过for循环从第一个数组元素开始,通过sum和while循环来保证for的当前元素能从2个元素的和判断是否是k的倍数一直到当前元素的n个元素之和判断是否是k的倍数
如果是倍数,则返回true
但是显然这个算法的时间复杂度是n^2的,超时了
第二次解答(瞅一瞅官方)
class Solution {
public boolean checkSubarraySum(int[] nums, int k) {
int m = nums.length;
if (m < 2) {
return false;
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(0, -1);
int remainder = 0;
for (int i = 0; i < m; i++) {
remainder = (remainder + nums[i]) % k;
if (map.containsKey(remainder)) {
int prevIndex = map.get(remainder);
if (i - prevIndex >= 2) {
return true;
}
} else {
map.put(remainder, i);
}
}
return false;
}
}
我对前缀和的理解:
前缀和类似于S(n) = a1+a2+…+an
而S(4)= a1+a2+a3+a4,S(6)=a1+a2+a3+a4+a5+a6
我们如果需要知道连续的序列的和比如a5+a6 , 显然,就是S(6)-S(4) = a5+a6
所以前缀和可以看成就是S(1),S(2),…,S(n)
有了这些前N项的和,我们可以快速求出需要的连续的序列的和
而前缀和的公式就是 S(n)=an+S(n-1)
思路
这里使用的是前缀和 + 哈希表的解法,前两天的解题中也是用了前缀和的思想。
首先使用Map存储余数,Map的存储格式是<余数:第一次出现的数组索引>
通过for循环,循环使用前缀和公式,这里的公式做了一些变形
S(n ) = (S(n-1)+an)%K
每个S(n)中存的都是余数。
我们只需要找到Map中余数相同的并且索引相差大于等于2的两个位置即可
找余数相同的原因:(看的别人的题解的自己理解)
设一个数组[23,2,4,6,7] ,K=6
这里我们可以看出来2,4是满足条件的,而满足这种条件的具有一下性质
23 = 3 * 6+5
23+2+4 = 4 * 6+5
前缀和 S(23,2,4) - S(23) = (4 * 6+5)-(3 * 6+5) = (4-3) *6
抽象一下:
设一个数组[a,b,c,d,e] ,K=k
这里我们设c,d是满足条件的,
则ab和abcd的余数K都是rem
而满足这种条件的具有一下性质
a+b = x * k+rem
a+b+c+d = y * k+rem
前缀和 S(abcd) - S(ab) = (y * k+rem)-(x * k+rem) = (y-x) *k
这显然如果余数相同则前缀和计算就会把余数约去,而剩下的就是一个K的倍数了
注意
学习的应该是使用前缀和解题的思想