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

题目

给定一个整数数组 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]

力扣上的题目,看了几个小时看懂了。基本所有的坑都踩到了。来看看我的解题思路吧!

解题思路

我们要养成这样一个习惯一看到 “子数组和” 就要想到 “前缀和”
什么是前缀和?
“前缀和” 就是数组第0项到当前项的总和,用一个数组接收。
拿示例举例,“前缀和”的第0项到第j项:
P[0]=A[0]
P[1]=A[0]+A[1]
P[i]=A[0]+A[1]+……+A[i]
P[j]=A[0]+A[1]+……+A[j]
巧妙的转化题目,我们要知道任意的子数组之和,也就是:
A[i]+……+A[j]=P[j]-P[i-1] (0<i<j)
这样问题就转化成求(P[j]-P[i-1])%K=0的次数(%在Java里是求余)

在这里又要引入同余定理,也就是:
(a-b)/m为整数,则有a%m=b%m。这就是同余嘛。
这里(a-b)/m为整数其实就是(a-b)%m=0与上式(P[j]-P[i-1])%K=0类似。
我们把同余定理倒着用:只要a%m=b%m,就有(a-b)%m=0。
也就是只要P[i-1]%K=P[j]%K,就有(P[j]-P[i-1])%K=0。
或者说只要P有同余就有(P[j]-P[i-1])%K=0 (P就是P[j]%K (0<j<A.length))

这里题目就大致清楚了。下面讲的有点抽象我们结合示例看:
先将要用到的数据算出来
A = [4,5,0,-2,-3,1]
P = [4,9,9,7,4,5] (P是“前缀和”)
P=[4,4,4,2,4,0] (P就是P[j]%K (0<j<A.length))

就是这里想了半天才想明白!!!
这里我把同余分为一般同余特殊同余
一般同余
示例中一般同余为4,且有4个。4个中每两个可以组成可被 K 整除的(连续、非空)子数组,这就转化成排列组合问题。
以P[4]为P[j]有3种:
P[0]与P[4],P[1]与P[4],P[2]与P[4]
以P[2]为P[j]有2种:
P[0]与P[2],P[1]与P[2]
以P[1]为P[j]有1种:
P[0]与P[1]
抽象一下就是(同余数-1)+(同余数-2)+……+0
特殊同余
也就是当P=0时,它自己就是可被 K 整除的(连续、非空)子数组。
但我们不能直接加上P=0的个数,因为它同时还满足一般同余

假设: 在一开始将P=0的个数加1

P=0的个数实际为1时,P=0的个数为2则
(同余数-1)+(同余数-2)+……+0=1+0=1

P=0的个数实际为2,P=0的个数为3则
(同余数-1)+(同余数-2)+……+0=2+1=3

P=0的个数实际为3,P=0的个数为4则
(同余数-1)+(同余数-2)+……+0=3+2+1=6

验证一下确实如此。

class Solution {
    public int subarraysDivByK(int[] A, int K) {
    //用HashMap来存数据 键为余,键值为余出现的次数
        Map<Integer, Integer> record = new HashMap<>();
        //这里是为了满足”特殊同余“在一开始将P余=0的个数加1
        record.put(0, 1);
        int sum = 0, ans = 0;
        for (int elem: A) {
        //sum为P[j]
            sum += elem;
            // 注意 Java 取模的特殊性,当被除数为负数时取模结果为负数,需要纠正
            int modulus = (sum % K + K) % K;
            /*判断modulus是否是record里的键,是same就为原有键值,否same就为0
            
            */
            int same = record.getOrDefault(modulus, 0);
            ans += same;
            record.put(modulus, same + 1);
        }
        return ans;
    }
}

总结

这道题主要考察“前缀和”与“同余定理”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值