2845. 统计趣味子数组的数目
1. 题目描述
给你一个下标从 0 开始的整数数组 nums ,以及整数 modulo 和整数 k 。
请你找出并统计数组中 趣味子数组 的数目。
如果 子数组 nums[l…r] 满足下述条件,则称其为 趣味子数组 :
在范围 [l, r] 内,设 cnt 为满足 nums[i] % modulo == k 的索引 i 的数量。并且 cnt % modulo == k 。
以整数形式表示并返回趣味子数组的数目。
注意:子数组是数组中的一个连续非空的元素序列。
2. 思路
前缀和+关联哈希+取模
2.1 前置思考:
-
新建一个数组a,长度与nums相同。
-
在符合
nums[i]%modulo==k
的下标位置处,令a[i]=1
,其余为0 -
此时问题转化成了求a数组中 满足1的个数
cnt%modulo==k
的子数组 个数 -
计算数组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]
的关系基础上,对i
和j
的关系也有要求。下面举一些简单的例子。
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. 对用题单
- 和为 K 的子数组
- 和可被 K 整除的子数组
- 连续的子数组和
- 连续数组