Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Number of subarrays divisible by k

I had the following question in an interview and, in spite of the fact that I gave a working implementation, it wasn't efficient enough.

A slice of array A is any pair of integers (P, Q) such that 0 ≤ P ≤ Q < N. A slice (P, Q) of array A is divisible by K if the number A[P] + A[P+1] + ... + A[Q−1] + A[Q] is divisible by K.

The function I was asked to write, had to return the number of slices divisible by K. The expected time complexity was O(max(N, K)) and space complexity was O(K).

My solution was the simplest, one loop inside another and check every slice: O(n^2)

I have been thinking but I really can't figure out how can I do it in O(max(N, K)).

It may be a variant of the subset sum problem, but I don't know how to count every subarray.

EDIT: Elements in array could be negatives. Here is an example:

A = {4, 5, 0, -2, -3, 1}, K = 5  Function must return 7, because there are 7 subarrays which sums are divisible by 5 {4, 5, 0, -2, -3, 1} {5} {5, 0} {5, 0, -2, -3} {0} {0, -2, -3} {-2, -3} 
like image 913
antoniobg Avatar asked May 17 '13 09:05

antoniobg


People also ask

How do you find the number of Subarrays whose Max K is?

// Stores count of subarrays with max <= k - 1. int count1 = totalSubarrays(arr, n, k - 1); // Stores count of subarrays with max >= k + 1. int count2 = totalSubarrays(arr, n, k);

How do you find the number of Subarrays?

The total number of subarrays in an array of size N is N * (N + 1) / 2. The count of subarrays with an odd product is equal to the total number of continuous odd elements present in the array. Therefore, count of subarrays with even product = (Total number of subarrays – Subarrays with the odd product).

How do you count Subarrays in an array?

We can easily calculate the number of sub-arrays of an array which has all 1s by using the formula n*(n+1)/2, where n is the length of the array with all 1s. Calculate the length of every sub-array which has all 1s and increment the count variable by length*(length+1)/2.


2 Answers

As you are only interested in numbers divisible by K, you can do all computations modulo K. Consider the cumulative sum array S such that S[i] = S[0] + S[1] + ... + S[i]. Then (P, Q) is a slice divisible by K iff S[P] = S[Q] (remember we do all computations modulo K). So you just have to count for each possible value of [0 ,..., K-1] how many times it appears in S.

Here is some pseudocode:

B = new array( K ) B[0]++ s = 0 for i = 0 to N - 1   s = ( s + A[i] ) % K   B[s]++ ans = 0 for i = 0 to K - 1   ans = ans + B[i] * ( B[i] - 1 ) / 2 

Once you know that they are x cells in S that have value i, you want to count the number of slices the start in a cell with value i and ends in a cell with value i, this number is x ( x - 1 ) / 2. To solve edge problems, we add one cell with value 0.

What does x ( x - 1 ) / 2 stands for: Let's assume our array is [4, 5, 0] and frequency of 4 as prefix sum is x, which is 3 in this case. Now we can conclude from value of x, that there are at least x-1 numbers which are either divisible by k or have mod k equals to 0. Now total possible sub arrays out of these x-1 numbers are 1 + 2 + 3 ... + ( x - 1 ) which is ( ( x - 1 ) * ( ( x - 1 ) + 1 ) / 2 . (Standard formula for summation from 1 to N where N stands for ( x - 1 ).

like image 185
Thomash Avatar answered Oct 07 '22 10:10

Thomash


Here is a Java implementation of the solution proposed by @Thomash.

The second loop is not necessary, because we can directly increase the answer by the current value and then increment it.

To avoid negative array index, we also have to adjust module computation.

public static int countSubarrays(int[] nums, int k) {     int[] cache = new int[k];     cache[0]++;     int s = 0, counter = 0;     for (int i = 0; i < nums.length; i++) {         s = ((s + nums[i]) % k + k) % k;         counter += cache[s];         cache[s]++;     }      return counter; } 
like image 44
Konstantin Milyutin Avatar answered Oct 07 '22 09:10

Konstantin Milyutin