Subarray Sums Divisible by K - Complete Solution Guide
Subarray Sums Divisible by K is LeetCode problem 974, a Medium level challenge. This complete guide provides step-by-step explanations, multiple solution approaches, and optimized code in python3, java, cpp, c.
Problem Statement
Given an integer array nums and an integer k , return the number of non-empty subarrays that have a sum divisible by k . A subarray is a contiguous part of an array. Example 1: Input: nums = [4,5,0,-2,-3,1], k = 5 Output: 7 Explanation: There are 7 subarrays with a sum divisible by k = 5: [4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3] Example 2: Input: nums = [5], k = 9 Output: 0 Constraints: 1 <= nums.length <= 3 * 10 4 -10 4 <= nums[i] <= 10 4 2 <= k <= 10 4
Detailed Explanation
The problem asks us to find the number of non-empty subarrays in a given integer array `nums` whose sum is divisible by a given integer `k`. A subarray is a contiguous sequence of elements within the array. We need to count how many such subarrays exist.
Solution Approach
The solution uses the concept of prefix sums and modular arithmetic. It iterates through the array `nums`, calculates the prefix sum at each index, and then computes the remainder of the prefix sum when divided by `k`. It uses an array `remainder_counts` of size `k` to store the frequency of each remainder. For each element, it increments the count of subarrays that can be formed using the current prefix sum and the existing prefix sums with the same remainder. The key idea is based on the property that if two prefix sums have the same remainder when divided by k, then their difference (which represents the sum of the subarray between them) is divisible by k.
Step-by-Step Algorithm
- Step 1: Initialize an array `remainder_counts` of size `k` with all elements set to 0. Set `remainder_counts[0]` to 1 because an empty subarray has a sum of 0, which is divisible by `k`.
- Step 2: Initialize `count` to 0 to keep track of the number of subarrays with sums divisible by `k`. Initialize `prefix_sum` to 0.
- Step 3: Iterate through the `nums` array.
- Step 4: In each iteration, update `prefix_sum` by adding the current element.
- Step 5: Calculate the `remainder` of the `prefix_sum` when divided by `k` using the modulo operator (`%`).
- Step 6: Handle negative remainders by adding `k` to the remainder until it becomes non-negative (only needed in Java, C, and C++).
- Step 7: Increment `count` by the value stored at `remainder_counts[remainder]`. This is because `remainder_counts[remainder]` represents the number of subarrays that end before the current index and have the same remainder as the current prefix sum. Therefore any of these will be the start of a valid subarray.
- Step 8: Increment `remainder_counts[remainder]` by 1 to update the count of the current remainder.
- Step 9: After iterating through all elements, return `count`.
Key Insights
- Insight 1: The sum of a subarray from index i to j is divisible by k if (prefix_sum[j+1] - prefix_sum[i]) % k == 0. This is equivalent to prefix_sum[j+1] % k == prefix_sum[i] % k.
- Insight 2: Using a hash table (or in this case, an array acting as a hash table since remainders are bounded) to store the count of each remainder modulo k allows us to efficiently find subarrays with sums divisible by k.
- Insight 3: When calculating the remainder with the modulo operator, it's important to handle negative remainders correctly to ensure they are non-negative. In languages like Java and C++, negative remainders need special handling.
Complexity Analysis
Time Complexity: O(n)
Space Complexity: O(k)
Topics
This problem involves: Array, Hash Table, Prefix Sum.
Companies
Asked at: Citadel, Expedia, Tinkoff, thoughtspot.