同余定理+前缀和解题技巧

相关题目

和可被 K 整除的子数组
连续的子数组和
Delete Sublist to Make Sum Divisible By K

思路

前缀和就不单独说了,同余定理贴一个百度百科

同余定理

数论中的重要概念。给定一个正整数m,如果两个整数a和b满足a-b能够被m整除,即(a-b)/m得到一个整数,那么就称整数a与b对模m同余,记作a≡b(mod m)。对模m同余是整数的一个等价关系。
转换成数学语言如下述所示

	/*
	假设有a,b,k三个数
	满足 a%k == b%k
	则 (a-b)%k == 0
	且为充要条件
	*/

题1

我们拿相关题目的第一题为例
题1整体思路

代码实现

class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) {
        int count = 0;
        unordered_map<int,int> hash;
        hash[0] = 1;   //自身能被整除需要直接计数
        int cursum = 0;
        for(int i = 0;i<nums.size();i++){
            cursum += nums[i];
            int mod = (cursum%k+k)%k;
            if(hash.count(mod)) count += hash[mod];
            hash[mod]++;
        }
        return count;
    }
};

时间复杂度为O(n),而暴力求解时间复杂度为O(n^2)

相关题目第2、3题主要就是在上述基础上添加了一些条件,可以再讲讲第三题

题3

题2整体思路

代码实现

int solve(vector<int>& nums, int k) {
    int sum = accumulate(nums.begin(),nums.end(),0);
    int target = sum%k;
    unordered_map<int,int> hash;
    hash[0]=-1;
    int presum = 0;
    int minlen = nums.size();
    for(int i = 0;i<nums.size();i++){
        presum += nums[i]; //当前前缀和
        int mod = presum%k;
        hash[mod] = i;
        //去找有没有前缀和的余数和当前余数相等,有就返回长度
        if(hash.count((presum-target)%k)){
            minlen = min(minlen,i-hash[(presum-target)%k]);
        }
    }
    return minlen == nums.size() ? -1:minlen;  //cannot delete entire list
}

题2

今天做了一下那就加上来吧

代码

class Solution {
public:
    bool checkSubarraySum(vector<int>& nums, int k) {
        unordered_map<int,int> hash;
        hash[0] = -1;
        int cursum = 0;
        for(int i = 0;i<nums.size();i++){
            cursum+=nums[i];
            int mod = cursum%k;
            //不能更新,只能加入
            if(hash.count(mod) && i-hash[mod]>1) return true;
            if(hash.count(mod) == 0) hash[mod] = i;
        }
        return false;
    }
};

这里的hash存储的是第一次出现新余数时的 i,意味着如果再次出现相同的mod,我们不能更新。因为这一题需要长度至少为2,意味着我们要让 ij 的差值尽可能大。当再次出现相同余数时,比较 ij 的插值,大于等于2即可返回true

后注

c++中负数取余规则是一律向0取余,那么当除数和被除数符号不相等时就会出现问题,例如

	int a = -1;
	int b = 2;
	cout << a % b;           //结果为-1
	cout <<(a % b + b) % b;  //结果为1

所以我们需要通过第二个公式来获得正确的余数

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值