前缀和变形 + 哈希表

发布时间 2023-09-13 00:34:58作者: value0

前缀和变形 + 哈希表

统计趣味子数组的数目

解题思路:

\[s_i = \sum_{i = 1} ^ n{nums[i] \% module == k} \]

题目求满足$s_r - s_{l-1} \equiv k \pmod {module} $的子数组的个数。

公式转换:

\[\begin{align} &s_r - s_{l-1} \equiv k \pmod {module}\\ &(s_r - s_{l-1}) \%module = k\\ &((s_r \% module-s_{l-1}\%module)\%module + module)\%module = k\\ &s_r\%module - k = s_{l-1}\%module\\ &((s_r - k)\%module + module)\%module =s_{l-1}\%module \end{align} \]

由上述公式建立哈希表,单次遍历(因为\(l<r\))判断即可。

时间复杂度: \(O(n)\)

代码:

class Solution {
public:
    long long countInterestingSubarrays(vector<int>& nums, int modulo, int k) {
        int n = nums.size();
        unordered_map<long long,int> q;
        q[0] = 1;
        long long s = 0;
        long long ans = 0;
        for(int i = 0;i<n;i++)
        {
            s = (s + (nums[i] % modulo == k)) % modulo;
            ans += q[((s - k) % modulo + modulo) % modulo];
            q[s % modulo] ++;
        }
        return ans;
    }
};

和为 K 的子数组

解题思路:

设:

\[s_i = \sum_{i = 1} ^ i{nums[i]} \]

公式变换:

\[\begin{align} &s_r - s_{l-1} = k\\ &s_r - k = s_{l-1} \end{align} \]

时间复杂度: \(O(n)\)

代码:

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<long long,int> q;
        q[0] = 1;
        int n = nums.size();
        long long s = 0;
        long long ans = 0;
        for(int i = 0;i<n;i++)
        {
            s += nums[i];
            ans += q[s - k];
            q[s] ++;
        }
        return ans;
    }
};

和可被 K 整除的子数组

解题思路:

设:

\[s_i = \sum_{i = 1} ^ i {nums[i]} \]

公式变换:

\[\begin{align} (s_r - s_{l-1}) \% k &= 0\\ s_r \% k &= s_{l-1}\%k \end{align} \]

代码:

class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) {
        map<long long,int> q;
        q[0] = 1;
        long long s = 0;
        long long ans = 0;
        int n = nums.size();
        for(int i = 0;i<n;i++)
        {
            s = ((s + nums[i] % k) + k) % k;
            ans += q[s];
            q[s]++;
        }
        return ans;
    }
};

连续的子数组和

解题思路:

设:

\[s_i = \sum_{i = 1} ^ i {nums[i]} \]

公式变换:

\[\begin{align} (s_r - s_{l-1})\% k& = 0 ,&(r - i >=2)\\ s_r\%k &= s_{l-1}\%k,&(r-i>=2) \end{align} \]

代码:

class Solution {
public:
    bool checkSubarraySum(vector<int>& nums, int k) {
        int n = nums.size();
        map<long long,int> q;
        long long last = nums[0] % k;
        q[0] = 1;
        long long s = last;
        long long ans = 0;
        for(int i = 1 ;i<n;i++)
        {
            s = (s + nums[i]) % k;
            if(q.count(s))
            {
                return true;
            }
            q[last]++;
            last = s;
        }
        return false;
    }
};

连续数组

解题思路:

设:

\[s_i = \sum_{i=1}^i{nums[i] == 1} \]

公式变换:

\[\begin{align} 2*(s_r-s_{l-1})&=r-l+1\\ 2 *s_r-r&=2*s_{l-1}+1 \end{align} \]

代码:

class Solution {
public:
    int findMaxLength(vector<int>& nums) {
        unordered_map<long long,long long> q;
        q[1] = -1;
        int n = nums.size();
        long long s = 0;
        long long cur = 0;
        long long ans = 0;
        for(int i = 0;i<n;i++)
        {
            s = s + nums[i];
            cur = 2 * s - i;
            if(q.count(cur))
            {
                ans = max(ans,i - q[cur]);
            }
            else
            {
                q[cur] = i;
            }
        }
        return ans;
    }
};