存储剩余物后的直觉?

时间:2018-03-25 00:44:21

标签: c++ algorithm

我正在尝试解决LeetCode.com上的问题:

  

给定一个非负数列表和一个目标整数k,写一个函数来检查该数组是否有一个大小至少为2的连续子数组,总和达到k的倍数,即总和为n * k其中n也是整数。例如,如果[23, 2, 4, 6, 7]k=6,则输出应为True,因为[2, 4]是大小为2的连续子数组,总计最多为6。

我想了解以下solution

class Solution {
public:
    bool checkSubarraySum(vector<int>& nums, int k) {
        int n = nums.size(), sum = 0, pre = 0;
        unordered_set<int> modk;
        for (int i = 0; i < n; ++i) {
            sum += nums[i];
            int mod = k == 0 ? sum : sum % k;
            if (modk.count(mod)) return true;
            modk.insert(pre);
            pre = mod;
        }
        return false;
    }
};

我了解到我们正在尝试将0, (a/k), (a+b)/k, (a+b+c)/k等存储到hashSet(k!= 0)中,并且我们在 next << / em>迭代,因为我们希望子阵列大小至少为2

但是,这是如何保证我们得到一个元素总和为k的子数组?什么数学属性保证了这一点?

2 个答案:

答案 0 :(得分:3)

集合modk逐渐填充从数组开始处开始的连续子数组的所有和(被视为模k)。

关键的观察是:

  

a-b = n * k表示某些自然n iff
  a-b≡0mod k iff
   a≡bmod k

所以如果一个连续的子数组nums [i_0] ... nums [i_1],总和最多为0模k,那么两个子数组nums [0] .. nums [i_0]和nums [i_0 + 1 ] .. nums [i_1]有相同的总和模数k。

因此,如果从数组开头开始的两个不同的子数组具有相同的总和,则模数为k就足够了。

幸运的是,只有k个这样的值,所以你只需要使用一组大小为k。

一些挑剔:

  • 如果n> k,你无论如何都会有一个合适的子阵列(pigeon-hole principle),所以循环实际上永远不会迭代超过k + 1次。
  • 这里不应该涉及任何类别,这没有任何意义。
  • 连续,不连续。数组和子数组是离散的,不能连续......

答案 1 :(得分:0)

总和的模块基数k等于模块基数k的总和的模块k

(a + b)%k =(a%k + b%k)%k

(23 + 2)%6 = 1
((23%6)+(2%6))%6 =(5 + 2)%6 = 1

modk存储您迭代计算的所有模块。如果在迭代中我得到一个在i-m处计算的重复模块,这意味着你添加了m个元素的子序列,其中和是k的倍数

 i=0 nums[0] = 23     sum = 23    sum%6 = 5     modk = [5]
 i=1 nums[1] = 2      sum = 25    sum%6 = 1     modk = [5, 1]
 i=2 nums[2] = 4      sum = 29    sum%6 = 5     5 already exists in modk (4+2)%6 =0