前缀和+哈希表——974. 和可被 K 整除的子数组

前缀和+哈希表——974. 和可被 K 整除的子数组_第1张图片

文章目录

    • 1. 题目
    • 2. 算法原理
      • 解法一:暴力枚举
      • 解法二:前缀和 + 哈希表
    • ⛳3. 代码实现

1. 题目

题目链接:974. 和可被 K 整除的子数组 - 力扣(LeetCode)

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

子数组 是数组的 连续 部分。

示例 1:

输入:nums = [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]

示例 2:

输入: nums = [5], k = 9
输出: 0

提示:

  • 1 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • 2 <= k <= 104

2. 算法原理

解法一:暴力枚举

暴力枚举出所有的子数组,然后判断是否有符合条件的,时间复杂度为O(n2)。

本题不可使用滑动窗口,因为这里面包含了负数0,不具有单调性

解法二:前缀和 + 哈希表

Tips:

  • 同余定理:(a-b)/p = k......0,当a-b的差能够被p整除,那么a%p = b%p
    前缀和+哈希表——974. 和可被 K 整除的子数组_第2张图片
  • 在C++中,负数%正数的结果为负数,如果要将其修正,只需要加上取模的这个数即可a%p + p,但为了正负统一,我们再模一下即可(a%p+p)%p
    前缀和+哈希表——974. 和可被 K 整除的子数组_第3张图片
    C++和JAVA都需要修正,Python不需要
    在这里插入图片描述

有了上面的知识,这题其实就和此篇文章的题目类似了:前缀和+哈希表——560. 和为 K 的子数组,有兴趣可以看一下

相当于只要找到以i为结尾的所有子数组的前缀和这个区间是否有能被k整除的子数组,即(sum-x) % k = 0

前缀和+哈希表——974. 和可被 K 整除的子数组_第4张图片

这个正好对应上面的同余定理,所以就变成了找出在[0,i-1]区间内,有多少个余数等于sum%k

注意修正余数(sum%k+k)%k

这里哈希表里面映射关系就是前缀和的余数次数

细节问题:

  • 前缀和加入哈希表的时机:不能一股脑将前缀和全部丢进哈希表,我们只需要计算在i位置之前,哈希表里面只保存[0,i-1]位置的前缀和。
  • 不用真正的创建一个前缀和数组,只需借助一个临时变量,记录这个位置之前的前缀和,计算完毕之后,再更新一下这个临时变量。
  • 当整个前缀和为k时,我们需要在哈希表中默认有一个余数为0的位置hash[0]=1,以防这个情况被忽略。

⛳3. 代码实现

class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k)
    {
        unordered_map<int,int> hash;
        hash[0] = 1;	//0的处理余数
        int sum = 0;
        int ret = 0;
        for(auto e:nums)
        {
            sum+=e;
            int r = (sum%k+k)%k;	//修正余数
            if(hash.count(r))  ret+=hash[r];
            hash[r]++;
        }
        return ret;
    }
};

你可能感兴趣的:(原创,刷题,散列表,数据结构,哈希算法,前缀和)