力扣2845(统计趣味子数组的数目)

2845. 统计趣味子数组的数目

1. 题目描述

给你一个下标从 0 开始的整数数组 nums ,以及整数 modulo 和整数 k 。

请你找出并统计数组中 趣味子数组 的数目。

如果 子数组 nums[l…r] 满足下述条件,则称其为 趣味子数组 :

在范围 [l, r] 内,设 cnt 为满足 nums[i] % modulo == k 的索引 i 的数量。并且 cnt % modulo == k 。
以整数形式表示并返回趣味子数组的数目。

注意:子数组是数组中的一个连续非空的元素序列。

2. 思路

前缀和+关联哈希+取模

2.1 前置思考:
  1. 新建一个数组a,长度与nums相同。

  2. 在符合nums[i]%modulo==k的下标位置处,令a[i]=1,其余为0

  3. 此时问题转化成了求a数组中 满足1的个数cnt%modulo==k的子数组 个数

  4. 计算数组a的前缀和数组,此时问题变成了求有多少个(i,j),满足(prefix[i]-prefix[j])%modulo==k,(0<=j<i)

一个可行的思路是,遍历前缀和数组,每当遍历到i时,求符合(prefix[i]-prefix[j])%modulo==k,(0<=j<i)j的个数,自然暴力遍历是一种解决方式,但是时间复杂度就在 O ( n 2 ) O(n^2) O(n2)了。需要优化这个搜索j的时间复杂度,目前是 O ( n ) O(n) O(n),要么优化到 O ( l o g n ) O(logn) O(logn),要么优化到 O ( 1 ) O(1) O(1)

优化搜索时间从 O ( n ) O(n) O(n) O ( l o g n ) O(logn) O(logn),很容易想到二分,但这里二分好像排不上用场。因为这是一个取模后等于k的表达式,如果没有取模操作,只是简单的减法操作,貌似二分就能排上用场。

2.2 关联哈希

所以这道题引出一个很经典的题型,就是关联哈希(这个叫法是笔者自己取的方便记忆,勿喷)。就是找到数组值a[i]a[j]之间的某种关系,通常是差值,取模,整除等等,又或者在a[i]a[j]的关系基础上,对ij的关系也有要求。下面举一些简单的例子。

a[i]-a[j] == k
(a[i]-a[j]) % modulo == 0
(a[i]-a[j]) % modulo == 0,i-j>=2
i-j-(a[i]-a[j]) == a[i]-a[j]

得到这样的关系后,需要将式子进行转化,得到关于a[j]的表达式,为什么是a[j],因为遍历到i时,a[i]是已知的,需要找满足条件的a[j]。上述的几个例子经过转换变成这样:

a[j] == a[i] - k
a[j] % modulo == a[i] % modulo
a[j] % modulo == a[i] % modulo (与上相同)
i-2a[j] == j-2a[i]

每当遍历完一个i后,将对应表达式值作为key存入哈希表,并将其val++,后续的i在搜索时,就可以直接查找哈希表中对应的key了。

2.3 结合本题

而本题中的关系其实已经找到了,即(prefix[i]-prefix[j])%modulo==k,显然需要对这个式子进行一下转化:

(prefix[i]-prefix[j])%modulo==k 
-> (prefix[i]-prefix[j])%modulo==k%modulo
-> prefix[i]%modulo - prefix[j]%modulo == k%modulo
-> prefix[j]%modulo == prefix[i]%modulo-k%modulo
-> prefix[j]%modulo == (prefix[i]-k)%modulo

使用map记录值为prefix[j]%modulo值的下标个数

3. 代码

class Solution {
public:
    long long countInterestingSubarrays(vector<int>& nums, int modulo, int k) {
        int a[100010]={0};
        int prefix[100010]={0};
        unordered_map<int, int> m;
        m[0]++;
        long long res = 0;
        for(int i=1;i<=nums.size();i++){
            if(nums[i-1]%modulo==k)a[i-1]=1; //求数组a
            prefix[i] = prefix[i-1]+a[i-1];  //求a的前缀和数组
            int x=(prefix[i]-k)%modulo;
            if(m.count(x)!=0)res+=m[x];    // 查找满足prefix[j]%modulo == (prefix[i]-k)%modulo的下标j的个数
            m[x]++;    
        }
        return res;
    }
};

4. 对用题单

  1. 和为 K 的子数组
  2. 和可被 K 整除的子数组
  3. 连续的子数组和
  4. 连续数组
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值