当前位置:   article > 正文

【LeetCode专题】前缀和+哈希表_前缀和哈希表第一项为1

前缀和哈希表第一项为1

LeetCode 560. 和为 K 的子数组

题目 示例

输入:nums = [1,1,1], k = 2
输出:2

输入:nums = [1,2,3], k = 3
输出:2
  • 1
  • 2
  • 3
  • 4
  • 5

思路:

利用前缀和sum记录当前的和,区间[l,r]的和为: s[r+1] - s[l]
nums    1 0 1 1 0 2  [k = 2]
sum   0 1 1 2 3 3 5
  • 1
  • 2
  • 3
利用哈希表map来存储 [历史前缀和], 题目要找的是 [当前前缀和 - 历史前缀和 == k] 的次数
所以需要遍历当前前缀和,如果 [sum - k] 出现在map中 cnt 次, 则结果 + cnt次,有 cnt 个子数组满足题意。
例如 i = 4时 sum = 3, sum - k = 1, mp[1] = 2, 说明有2个子数组满足, 即 [0,1,1]、[1,1]
  • 1
  • 2
  • 3

代码:

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int n = nums.size(), cnt = 0;
        int sum = 0;
        // map优化
        unordered_map<int, int> mp;
        // for(int i = 0; i < n; i++) s[i+1] = s[i] + nums[i]; // 必须遍历过程中计算前缀和
        mp[0] = 1; // 处理边界:前缀和为 0 出现了 1 次
        for(int r = 0; r < n; r++){
            sum += nums[r];
            if(mp.count(sum - k) > 0){
                cnt += mp[sum - k];
            }
            mp[sum] ++;
        }
        return cnt;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

LeetCode 525. 连续数组

题目 示例

输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
  • 1
  • 2
  • 3

思路

1 表示 +1
0 表示 -1
用前缀和来表示1和0的数量
同样, 用map来存储历史前缀和第一次出现时的[下标]
nums    0 1  0  0  1 1
sum  0 -1 0 -1 -2 -1 0
遍历前缀和,如果当前前缀和在历史前缀和中出现过,说明区间 [l,r] 具有相同的0和1,更新ans
否则,记录当前前缀和第一次出现时的下标pos
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

代码

class Solution {
public:
    int findMaxLength(vector<int>& nums) {
        int ans = 0, n = nums.size();
        unordered_map<int, int> mp;
        mp.insert({0,-1}); //初始 0 的 pos = -1
        int sum = 0; 
        for(int r = 0; r < n; r++){
            // 一边遍历一边计算前缀和
            sum += nums[r] == 1 ? 1 : -1; // core/
            if(mp.count(sum) > 0){
                ans = max(ans, r - mp[sum]);
            }
            else mp[sum] = r;
        }
        return ans;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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

题目 示例

输入: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]
  • 1
  • 2
  • 3
  • 4
  • 5

思路

[l,r]区间和: s[r+1] - s[l]
(s[r+1] - s[l]) % k == 0
<==> s[r+1] % k - s[l] % k == 0
<==> s[r+1] % k == s[l] % k
所以区间和 mod k == 0 的个数  等价于 前缀和s[r+1] % k 等于 s[l] % K 的个数
例如:
nums    4  5 0 -2 -3 1     k = 5
sum   0 4  9 9  7  4 5
%k    0 1  1  1  2  1 0
mp[1] = 4, mp[0] =2
前缀和%k = 1的次数为4, %k = 0的次数为2
cnt = ∑ N * (N-1) / 2 = 6 + 1 = 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

代码

class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) {
        // num   4 5 0 -2 -3 1
        // s   0 4 9 9  7  4 5
        //%k   0 1 1 1  2  1 0
        // [l, r] -->  s[r+1] - s[l]
        // s[r+1] - s[l] % k == 0 ?
        // s[r+1] % k == s[l] % k ?
        // mp[1] = 4 mp[0]=2 
        // n*(n-1) /2 
        int n = nums.size();
        long long cnt = 0;
        int s[n+1];
        s[0] = 0;
        unordered_map<int, long long> mp;
        for(int i = 0; i < n; i++) s[i+1] = s[i] + nums[i];
        for(int i = 0; i <= n; i++){
            while(s[i] < 0) s[i] = (s[i] + k) % k;
            s[i] = (s[i] + k) % k;
            mp[s[i]] ++;
        } 
        for(auto [k, v] : mp){
            // cout << k << " " << v << endl;
            cnt += (v - 1) * v / 2;
        }
        return cnt;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

LeetCode 523. 连续的子数组和

题目 示例

输入:nums = [23,2,4,6,7], k = 6
输出:true
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6 。
  • 1
  • 2
  • 3

思路

思路同上一题974,
如果map中存在 %k 后相同的历史前缀和,说明 [l,r] 区间和为k的倍数,只需再判断区间长度是否>=2即可
  • 1
  • 2

代码

class Solution {
public:
    bool checkSubarraySum(vector<int>& nums, int k) {
        // (s[r+1] - s[l]) % k == 0
        // s[r+1] % k == s[l] % k  &&  r + 1 - l >= 2
        int n = nums.size();
        int s[n+1];
        s[0] = 0;
        for(int i = 0; i < n; i++) s[i+1] = s[i] + nums[i];
        for(int i = 0; i <= n; i++) s[i] = s[i] % k;
        unordered_map<int, int> mp;
        for(int r = 0; r <= n; r ++){
            if(mp.count(s[r]) > 0 && r - mp[s[r]] >= 2){
                return true;
            }
            if(mp.count(s[r]) == 0) mp[s[r]] = r; // 记录pos
        }
        return false;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/547312
推荐阅读
相关标签
  

闽ICP备14008679号