leetcode 974. 和可被 K 整除的子数组【前缀和、同余定理】

首先题目中明确表示所求的子数组和连续、非空的,因此可以使用前缀和来对暴力的遍历进行简化。
为了直观地体现前缀和的作用,在不使用同余定理的前提下写出以下仅使用前缀和数组的方法,由于使用两重循环,因此该方法会超时。

class Solution {
    public int subarraysDivByK(int[] A, int K) {
        int len = A.length;
        int[] prefix = new int[len];
        int sum=0;
        int res=0;
        //对第一位特殊处理,因为prefix[-1]为0
        prefix[0] = A[0];
        for(int i=1; i<len; i++){
            prefix[i] = sum+A[i] + prefix[i-1];
            System.out.print(prefix[i] + ", ");
        }
        
        for(int i=0; i<len; i++){
        //此处对prefix[-1]特殊处理
            if( (prefix[i] -0) % K ==0){
                res++;
            }
            for(int j=1; j<=i; j++){
                if((prefix[i] - prefix[j-1]) % K ==0){
                    res++;
                }
            }
        }
        return res;

    }
}

为了进一步简化上述代码,引入同余定理。如果两个前缀和的对K的余数相等,那么这两个前缀和之差所得到的子数组之和必定可以被K整除。因此我们只需要计算K的每个余数所对应的的前缀和的出现次数就可以得到子数组的数量。

class Solution {
    public int subarraysDivByK(int[] A, int K) {
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);
        int prefix=0;
        int res=0;
        for(int i=0; i<A.length; i++){
            prefix = (prefix + A[i]) % K;
            if(prefix<0)prefix+=K;
            if(map.containsKey(prefix)){
                res += map.get(prefix);
                map.put(prefix, map.get(prefix)+1);
            }else{
                map.put(prefix, 1);
            }
        }
        return res;
    }
}

利用数组代替Map,能进一步提高代码效率。

class Solution {
    public int subarraysDivByK(int[] A, int K) {
        int[] map = new int[K];
        map[0]++;
        int prefix=0;
        int res = 0;
        for(int a : A){
            prefix = (a+prefix)%K;
            if(prefix<0)prefix+=K;
            res+=map[prefix]++;
        }
        return res;
    }
}

你可能感兴趣的:(刷题路漫漫)