[算法]LeetCode每日一题--974和可被 K 整除的子数组

974. 和可被 K 整除的子数组

20200527

难度:中等

题目描述

给定一个整数数组 A,返回其中元素之和可被 K 整除的(连续、非空)子数组的数目。

示例:

输入:A = [4,5,0,-2,-3,1], K = 5
输出:7
解释:
有 7 个子数组满足其元素之和可被 K = 5 整除:
[4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]

提示:

  1. 1 <= A.length <= 30000
  2. -10000 <= A[i] <= 10000
  3. 2 <= K <= 10000

Solution

看到子数组和——想到前缀和,和 560. 和为K的子数组类似

  • 什么是前缀和?

    数组 第 0 项 到 当前项 的 总和,于是有preSum[i]=A[0]+A[1]+…+A[i]

    数组某项,可以表示为相邻前缀和之差:A[i]=preSum[i]−preSum[i−1]

    叠加多项,得到A[i]+…+A[j]=preSum[j]−preSum[i−1]

    preSum[-1] 为 0,此时:A[0]+A[1]+…+A[j]=preSum[j]

  • 这里的【前缀和】指的是:前j项和 mod K

    元素之和能被 K 整除的子数组数目,也就是求i,j的组合,使得第 i ~ j 项的和 mod K == 0

    (pre[j]-pre[i-1]) mod K == 0

    pre[j] mod K == pre[i-1] mod K

  • 用哈希表存【前缀和】以及对应出现的次数

    • key:前缀和mod K
    • value:这个余数值出现了几次
方法一:哈希表 + 逐一统计
class Solution {
    public int subarraysDivByK(int[] A, int K) {
        Map<Integer, Integer> preSumFreq = new HashMap<>();
        int modK = 0;
        int count = 0;
        int sum = 0;
        preSumFreq.put(0, 1);//[前缀和 mod K = 0]已经出现 1 次
        for(int num : A){
            sum += num;
            // modK = sum % K;
            modK = (sum % K + K) % K;
            if(preSumFreq.containsKey(modK)){
                count += preSumFreq.get(modK);
            }
            preSumFreq.put(modK, preSumFreq.getOrDefault(modK, 0) + 1);
        }
        return count;
    }
}
  • 要注意是取余的求法,Java中负数取模时,当被取模数为负数时取模结果为负数,需要纠正,因为如果有负数的话会导致哈希表找不到正确的余数。modK = (sum % K + K) % K;这样就可以把求出来的负数模变成正数,取正余数。
  • 负数参与的取模运算规则:先忽略负号,按照正数运算之后,被取模的数是正数结果就取正,反之取负。
方法二:哈希表 + 单次统计

方法一是在遍历的时候逐一累加答案。

方法二和上面的思路类似,只是最后再统计答案,用排列组合来计算。

哈希表中(key,value)键值对,key这个前缀和出现了value次,要从这value次中任取两次,分别作为子数组的头和尾,例如value=4次,也就是
( 4 2 ) = 6 \binom{4}{2} = 6 (24)=6
则当前value对应的符合要求的子数组个数为value*(value-1)/2

class Solution {
    public int subarraysDivByK(int[] A, int K) {
        Map<Integer, Integer> record = new HashMap<>();
        record.put(0, 1);
        int sum = 0;
        for (int elem: A) {
            sum += elem;
            int modulus = (sum % K + K) % K;
            record.put(modulus, record.getOrDefault(modulus, 0) + 1);
        }

        int ans = 0;
        for (Map.Entry<Integer, Integer> entry: record.entrySet()) {
		    ans += entry.getValue() * (entry.getValue() - 1) / 2;
		}
        return ans;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值