Facebook Pixel

2845. Count of Interesting Subarrays

Problem Description

You are given a 0-indexed integer array nums, an integer modulo, and an integer k.

Your task is to find the count of subarrays that are interesting.

A subarray nums[l..r] is considered interesting if it satisfies the following condition:

  • Count how many indices i in the range [l, r] have the property that nums[i] % modulo == k. Let's call this count cnt.
  • The subarray is interesting if cnt % modulo == k.

In other words, you need to:

  1. For each subarray, count how many elements in that subarray leave remainder k when divided by modulo
  2. Check if this count itself leaves remainder k when divided by modulo
  3. If yes, the subarray is interesting

Return the total number of interesting subarrays in the array.

Note: A subarray is a contiguous non-empty sequence of elements within an array.

Example to clarify: If nums = [3, 2, 4], modulo = 2, and k = 1:

  • For subarray [3]: one element where 3 % 2 = 1 (equals k), so cnt = 1. Since 1 % 2 = 1 (equals k), this subarray is interesting.
  • For subarray [3, 2]: only one element where 3 % 2 = 1, so cnt = 1. Since 1 % 2 = 1, this subarray is interesting.
  • For subarray [2, 4]: zero elements satisfy the condition, so cnt = 0. Since 0 % 2 = 0 (not equal to k), this subarray is not interesting.
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is to transform this problem into a simpler one using prefix sums and modular arithmetic.

First, let's simplify what we're counting. Instead of working with the original array, we can create a binary array where each position is 1 if nums[i] % modulo == k, and 0 otherwise. Now the problem becomes: find subarrays where the sum of elements (count of 1s) satisfies sum % modulo == k.

For any subarray [l, r], the count of 1s equals prefix_sum[r] - prefix_sum[l-1]. We want this difference to satisfy: (prefix_sum[r] - prefix_sum[l-1]) % modulo == k

Rearranging this equation using modular arithmetic: prefix_sum[r] % modulo - prefix_sum[l-1] % modulo ≡ k (mod modulo)

This means: prefix_sum[r] % modulo ≡ (prefix_sum[l-1] % modulo + k) % modulo

Or equivalently: prefix_sum[l-1] % modulo ≡ (prefix_sum[r] % modulo - k) % modulo

This reveals the pattern: for each position r, we need to count how many previous positions l-1 have a prefix sum that, when taken modulo modulo, equals (current_prefix_sum - k) % modulo.

This naturally leads to using a hash table to track the frequency of each prefix_sum % modulo value as we traverse the array. For each position, we:

  1. Check how many previous prefix sums would make a valid subarray ending here
  2. Add the current prefix sum modulo value to our frequency map

We initialize with cnt[0] = 1 to handle subarrays starting from index 0, where the "previous" prefix sum is considered to be 0.

Learn more about Prefix Sum patterns.

Solution Approach

The implementation follows the prefix sum with hash table pattern:

Step 1: Transform the array

arr = [int(x % modulo == k) for x in nums]

We create a binary array arr where arr[i] = 1 if nums[i] % modulo == k, otherwise arr[i] = 0. This transformation simplifies our problem to counting subarrays with a specific sum property.

Step 2: Initialize the hash table

cnt = Counter()
cnt[0] = 1

We use a hash table cnt to track the frequency of prefix_sum % modulo values. Initialize cnt[0] = 1 to handle subarrays that start from index 0.

Step 3: Traverse and count

ans = s = 0
for x in arr:
    s += x
    ans += cnt[(s - k) % modulo]
    cnt[s % modulo] += 1

For each element in the transformed array:

  • Update the prefix sum s by adding the current element
  • Look up how many previous prefix sums would create a valid subarray ending at the current position. We need prefix sums that satisfy (s - prefix_sum) % modulo == k, which means we look for prefix_sum % modulo == (s - k) % modulo
  • Add this count to our answer
  • Update the hash table with the current prefix sum modulo value for future positions to use

The key insight is that (s - k) % modulo gives us the required remainder that previous prefix sums must have for the subarray to be interesting.

Time Complexity: O(n) where n is the length of the array, as we traverse once.

Space Complexity: O(min(n, modulo)) for the hash table, which stores at most modulo different remainder values.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through with nums = [3, 2, 4], modulo = 2, k = 1.

Step 1: Transform the array

  • Check each element: does nums[i] % 2 == 1?
  • 3 % 2 = 1 ✓, 2 % 2 = 0 ✗, 4 % 2 = 0
  • Transformed array: arr = [1, 0, 0]

Step 2: Initialize

  • cnt = {0: 1} (to handle subarrays from start)
  • ans = 0, s = 0 (prefix sum)

Step 3: Process each element

Index 0 (arr[0] = 1):

  • Update prefix sum: s = 0 + 1 = 1
  • Look for previous prefix sums with remainder (1 - 1) % 2 = 0
  • cnt[0] = 1, so add 1 to answer: ans = 1
  • Update cnt: cnt[1 % 2] = cnt[1] += 1
  • State: cnt = {0: 1, 1: 1}, ans = 1

Index 1 (arr[1] = 0):

  • Update prefix sum: s = 1 + 0 = 1
  • Look for previous prefix sums with remainder (1 - 1) % 2 = 0
  • cnt[0] = 1, so add 1 to answer: ans = 2
  • Update cnt: cnt[1 % 2] = cnt[1] += 1
  • State: cnt = {0: 1, 1: 2}, ans = 2

Index 2 (arr[2] = 0):

  • Update prefix sum: s = 1 + 0 = 1
  • Look for previous prefix sums with remainder (1 - 1) % 2 = 0
  • cnt[0] = 1, so add 1 to answer: ans = 3
  • Update cnt: cnt[1 % 2] = cnt[1] += 1
  • State: cnt = {0: 1, 1: 3}, ans = 3

Result: 3 interesting subarrays

Verification:

  1. [3] (indices 0-0): cnt=1, 1 % 2 = 1
  2. [3,2] (indices 0-1): cnt=1, 1 % 2 = 1
  3. [3,2,4] (indices 0-2): cnt=1, 1 % 2 = 1

Solution Implementation

1class Solution:
2    def countInterestingSubarrays(self, nums: List[int], modulo: int, k: int) -> int:
3        # Convert nums to binary array where 1 indicates element satisfies (num % modulo == k)
4        # This transforms the problem to counting subarrays with specific sum properties
5        satisfies_condition = [1 if num % modulo == k else 0 for num in nums]
6      
7        # Dictionary to store frequency of prefix sums modulo 'modulo'
8        # Key: prefix sum % modulo, Value: count of such prefix sums
9        prefix_sum_count = Counter()
10      
11        # Initialize with 0 to handle subarrays starting from index 0
12        # This represents the empty prefix with sum 0
13        prefix_sum_count[0] = 1
14      
15        # Initialize result counter and running prefix sum
16        result = 0
17        current_prefix_sum = 0
18      
19        # Iterate through the binary array
20        for value in satisfies_condition:
21            # Update the running prefix sum
22            current_prefix_sum += value
23          
24            # For a subarray to be interesting, we need:
25            # (current_prefix_sum - previous_prefix_sum) % modulo == k
26            # Which means: previous_prefix_sum % modulo == (current_prefix_sum - k) % modulo
27            # Count all valid previous prefix sums that can form an interesting subarray
28            target_remainder = (current_prefix_sum - k) % modulo
29            result += prefix_sum_count[target_remainder]
30          
31            # Add current prefix sum to the dictionary for future subarrays
32            prefix_sum_count[current_prefix_sum % modulo] += 1
33      
34        return result
35
1class Solution {
2    public long countInterestingSubarrays(List<Integer> nums, int modulo, int k) {
3        int n = nums.size();
4      
5        // Convert input list to binary array where 1 indicates the element satisfies nums[i] % modulo == k
6        int[] satisfiesCondition = new int[n];
7        for (int i = 0; i < n; i++) {
8            satisfiesCondition[i] = (nums.get(i) % modulo == k) ? 1 : 0;
9        }
10      
11        // Map to store frequency of prefix sum remainders when divided by modulo
12        // Key: remainder of prefix sum divided by modulo
13        // Value: count of such remainders seen so far
14        Map<Integer, Integer> prefixSumRemainderCount = new HashMap<>();
15      
16        // Initialize with 0 remainder having count 1 (empty prefix)
17        prefixSumRemainderCount.put(0, 1);
18      
19        long result = 0;
20        int prefixSum = 0;
21      
22        // Iterate through the binary array
23        for (int currentValue : satisfiesCondition) {
24            // Update running prefix sum
25            prefixSum += currentValue;
26          
27            // Calculate the target remainder we need to find
28            // We want subarrays where (prefixSum[j] - prefixSum[i]) % modulo == k
29            // This means prefixSum[j] % modulo - prefixSum[i] % modulo == k (mod modulo)
30            // So we look for prefixSum[i] % modulo == (prefixSum[j] - k) % modulo
31            int targetRemainder = ((prefixSum - k) % modulo + modulo) % modulo;
32          
33            // Add count of all valid subarrays ending at current position
34            result += prefixSumRemainderCount.getOrDefault(targetRemainder, 0);
35          
36            // Update the map with current prefix sum remainder
37            int currentRemainder = prefixSum % modulo;
38            prefixSumRemainderCount.merge(currentRemainder, 1, Integer::sum);
39        }
40      
41        return result;
42    }
43}
44
1class Solution {
2public:
3    long long countInterestingSubarrays(vector<int>& nums, int modulo, int k) {
4        int n = nums.size();
5      
6        // Convert array to binary representation: 1 if nums[i] % modulo == k, 0 otherwise
7        vector<int> satisfiesCondition(n);
8        for (int i = 0; i < n; ++i) {
9            satisfiesCondition[i] = (nums[i] % modulo == k) ? 1 : 0;
10        }
11      
12        // Map to store frequency of prefix sum remainders when divided by modulo
13        // Key: remainder of prefix sum when divided by modulo
14        // Value: count of such prefix sums
15        unordered_map<int, int> prefixSumModCount;
16        prefixSumModCount[0] = 1;  // Empty prefix has sum 0
17      
18        long long result = 0;
19        int prefixSum = 0;
20      
21        // Iterate through the binary array
22        for (int currentValue : satisfiesCondition) {
23            prefixSum += currentValue;
24          
25            // To find subarrays where (count % modulo == k), we need:
26            // (prefixSum[j] - prefixSum[i-1]) % modulo == k
27            // This means: prefixSum[i-1] % modulo == (prefixSum[j] - k) % modulo
28            // We look for previous prefix sums with the required remainder
29            int targetRemainder = ((prefixSum - k) % modulo + modulo) % modulo;
30            result += prefixSumModCount[targetRemainder];
31          
32            // Update the count for current prefix sum's remainder
33            prefixSumModCount[prefixSum % modulo]++;
34        }
35      
36        return result;
37    }
38};
39
1/**
2 * Counts the number of interesting subarrays where the count of elements
3 * satisfying (nums[i] % modulo === k) has a count c such that (c % modulo === k)
4 * @param nums - The input array of numbers
5 * @param modulo - The modulo value for calculations
6 * @param k - The target remainder value
7 * @returns The count of interesting subarrays
8 */
9function countInterestingSubarrays(nums: number[], modulo: number, k: number): number {
10    // Transform the array: 1 if element satisfies condition, 0 otherwise
11    const transformedArray: number[] = [];
12    for (const num of nums) {
13        transformedArray.push(num % modulo === k ? 1 : 0);
14    }
15  
16    // Map to store frequency of prefix sum remainders
17    // Key: prefix sum % modulo, Value: frequency count
18    const prefixSumModCount: Map<number, number> = new Map();
19    prefixSumModCount.set(0, 1); // Initialize with 0 remainder having count 1
20  
21    let interestingSubarrayCount: number = 0;
22    let currentPrefixSum: number = 0;
23  
24    // Iterate through transformed array to find valid subarrays
25    for (const value of transformedArray) {
26        currentPrefixSum += value;
27      
28        // Check if there exists a previous prefix sum that forms a valid subarray
29        // We need (currentSum - previousSum) % modulo === k
30        // Which means previousSum % modulo === (currentSum - k) % modulo
31        const targetRemainder: number = (currentPrefixSum - k + modulo) % modulo;
32        interestingSubarrayCount += prefixSumModCount.get(targetRemainder) || 0;
33      
34        // Update the frequency map with current prefix sum remainder
35        const currentRemainder: number = currentPrefixSum % modulo;
36        prefixSumModCount.set(currentRemainder, (prefixSumModCount.get(currentRemainder) || 0) + 1);
37    }
38  
39    return interestingSubarrayCount;
40}
41

Time and Space Complexity

The time complexity is O(n), where n is the length of the array nums. This is because:

  • Creating the arr list requires a single pass through nums with O(n) time
  • The main loop iterates through each element in arr exactly once, which is O(n)
  • Inside the loop, all operations (addition, dictionary lookup, dictionary insertion, modulo operation) are O(1)
  • Therefore, the overall time complexity is O(n)

The space complexity is O(n). This is due to:

  • The arr list stores n elements, requiring O(n) space
  • The cnt Counter (dictionary) can store at most min(n+1, modulo) distinct keys, since we're storing values modulo modulo. In the worst case, this is O(n) when modulo >= n+1
  • Other variables (ans, s, x) use O(1) space
  • Therefore, the overall space complexity is O(n)

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Modulo Operation for Negative Numbers

The most critical pitfall in this solution is handling the modulo operation when computing (current_prefix_sum - k) % modulo. In Python, the modulo operation handles negative numbers correctly (always returns non-negative results), but this behavior varies across programming languages.

Problem Example:

  • If current_prefix_sum = 1, k = 3, modulo = 5
  • (1 - 3) % 5 should give us 3 (the correct mathematical result)
  • In Python: (-2) % 5 = 3
  • In Java/C++: (-2) % 5 = -2

Solution: For languages that don't handle negative modulo correctly, use:

target_remainder = ((current_prefix_sum - k) % modulo + modulo) % modulo

2. Forgetting to Initialize the Hash Table with cnt[0] = 1

Without initializing cnt[0] = 1, subarrays starting from index 0 won't be counted correctly.

Problem Example:

  • nums = [3], modulo = 2, k = 1
  • The entire array [3] should be counted as interesting
  • Without cnt[0] = 1, this subarray would be missed

Solution: Always initialize the counter with cnt[0] = 1 before processing any elements.

3. Using the Wrong Target Calculation

A common mistake is looking for (s + k) % modulo instead of (s - k) % modulo.

Problem Example: The relationship is: (current_sum - previous_sum) % modulo == k This rearranges to: previous_sum % modulo == (current_sum - k) % modulo

Using addition instead would look for the wrong prefix sums and produce incorrect results.

Solution: Remember the mathematical relationship: we're looking for prefix sums where the difference equals k modulo modulo, so we subtract k from the current sum.

4. Updating Hash Table Before Counting

If you update cnt[current_prefix_sum % modulo] before checking cnt[(current_prefix_sum - k) % modulo], you might incorrectly count single-element subarrays when k == 0.

Problem Example:

  • When k = 0 and the current element satisfies the condition
  • If we update first, we'd count the current position as both start and end of a subarray

Solution: Always follow the order:

  1. Count valid subarrays using current state
  2. Update the hash table with current prefix sum
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Is the following code DFS or BFS?

void search(Node root) {
  if (!root) return;
  visit(root);
  root.visited = true;
  for (Node node in root.adjacent) {
    if (!node.visited) {
      search(node);
    }
  }
}

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More