题解
题意:给定一个整数数组 A,返回其中元素之和可被 K 整除的(连续、非空)子数组的数目。其中k<=10000
, A.length <= 10000
题解:连续非空的元素之和,最容易想到的就是求前缀和,利用pre[r]-pre[l]
的方式得到区间内满足条件的子数组和。然而前缀和遍历所有情况需要o(n^2)
的复杂度,因此,很多情况下,包括此题,这都是不可能直接做的,因此我们需要表示状态,然后进行状态的压缩
整除的状态 :一般而言,只要是要求不能遍历所有数据的,都是存在一个规律使得不需要遍历所有状态。比如此题,要求被K
整除,我们就需要考虑整除有什么性质,首先,在普通情况下(pre[r]-pre[l])%k == 0
意味着在(l,r)
之间的数组是可以被k
整除的,那么如何才能避免遍历所有情况呢?压缩状态个数 :这就要转换一下,利用同余定理,将上式转换为pre[l]%k==pre[r]%k
,这样有什么好处呢?在上式情况下,我们需要寻找l,r
的组合,而在此时,我们只需要记录mod = pre[r]%k
出现的时候,相同mod
在前缀里出现的次数即可,并不需要再重新定位l
的位置,因此,前缀和就不再需要记录,而是换成余数数组用于记录特定余数出现的次数即可,每次出现相同的余数x
,就把之前所有同余的数量加起来(每个区间都是不一样的),记为ans += mod[x]
总结 :此类题型一般是先找出前缀和的解法,然后抛弃前缀和,利用条件中存在的规律对状态进行压缩,一般是利用记录状态的方式来去除冗余计算。 实现:注意,mod[0]=1
,因为余数为0的时候本身就是整除的数;此外,注意余数是负数的情况,需要通过sum%k+k
来解决
class Solution {
public int subarraysDivByK ( int [ ] A, int K) {
int n = A. length;
int sum = 0 ;
int [ ] mod = new int [ K] ;
int res = 0 ;
mod[ 0 ] = 1 ;
for ( int i = 0 ; i < n; i++ ) {
sum = sum+ A[ i] ;
res += mod[ ( sum% K+ K) % K] ;
mod[ ( sum% K+ K) % K] ++ ;
}
return res;
}
}
相关题目