Hello, reader ๐๐ฝ ! Welcome to day 26 of the series on Problem Solving. Through this series, I aim to pick up at least one question everyday and share my approach for solving it.
Today, I will be picking up LeetCode's daily challenge problem: 523. Continuous Subarray Sum.
๐ค Problem Statement
- Given an array of integers and an integer k, check if there exists a subarray of at least size 2 whose sum is a multiple of k.
- Return true if such a subarray exists, else false.
0 is always a multiple of any k.
E.g.:
nums = [23,2,4,6,7], k = 6
=>true
[2,4]
is a subarray whose sum is divisible by k.
nums = [23,2,6,4,7], k = 6
=>true
[23,2,4,6,7]
is a subarray whose sum is divisible by k.
nums = [5,0,0,0], k = 3
=>true
[0, 0]
is a multiple of 3
๐ฌ Thought Process - Brute Force
- The brute force approach is to generate all the the continuous subarrays and check if any of their sum is a multiple of k.
- This works, but it's highly inefficient as it takes
O(n^2)
to generate all continuous subarrays.
๐ฌ Thought Process - HashMap and Running Sum
- This idea is based on a little bit of math that can be easier to understand visually.
- But before we move on to the visual, let's vaguely look at the motivation behind this approach.
- Suppose a subarray, let us call it
sum
, from i to j,0 <= i < j < n
is divisible by k =>sum % k == 0
.- let
sum1 = nums[0] + nums[1] + ... + nums[i-1] + nums[i]
- let
sum2 = nums[0] + nums[1] + .... + nums[j-1] + nums[j]
- then,
sum1 % k == sum2 % k
- then,
- let
sum1
andsum2
can lead to the same remainder on dividing by k only and only if there was a subarray sum in between that was a multiple of k.- We can keep calculating the contiguous running sum and use a hash table that stores
<remainder, index>
pairs. - Before adding the current
runningSum % k
, we check if the same remainder was already seen before and was at least 2 indices behind from current index. If yes, then that means there was some subarray between the index i (previous seen remainder) and j (current index), whose sum was a multiple of k.
Let's now look at it visually to understand a little deeper.
Two edge cases are if:
- The first element is itself a of k => In this case, we should not return true, since subarray size is only 1
- To handle cases when the entire subarray sum with size > 2 is a multiple of k, i.e.
sum % k == 0
Hence we add to the hashmap pair
0, -1
which is sort of a dummy to handle the above two cases.This edge case can be seen below:
If you think about this carefully, this is valid because there will only be
0 to k-1
remainders between 2 multiples of k.Now that this is clear, let's head over to the code.
๐ฉ๐ฝโ๐ป Solution - HashMap and Running Sum
Below is the code for the above approach using hash map and storing previously seen remainders.
class Solution { public boolean checkSubarraySum(int[] nums, int k) { if(nums == null || nums.length == 0) { return false; } int n = nums.length; HashMap<Integer, Integer> map = new HashMap(); map.put(0, -1); int sum = 0; for(int i = 0; i<n; i++) { sum += nums[i]; int remainder = sum % k; if(!map.containsKey(remainder) ) { map.put(remainder, i); } else { int j = map.get(remainder); if(i - j > 1) { return true; } } } return false; } }
Time Complexity: O(n) - n = length of array - Since we are only traversing the array once. Space Complexity: O(n) or O(k) - n = length of array - If n > k, then O(n) else O(k)
- You can find the code for this question in the GitHub repo here: 523. Continuous Subarray Sum.
Conclusion
That's a wrap for today's problem. If you liked my explanation then please do drop a like/ comment. Also, please correct me if I've made any mistakes or if you want me to improve something!
Thank you for reading!