Given an integer array nums and an integer k, determine whether the array contains a contiguous subarray that satisfies both conditions:
The subarray contains at least two elements.
The subarray sum is a multiple of
k.
Return true when such a subarray exists. Otherwise, return false.
Example 1
Input: nums = [23, 2, 4, 6, 7], k = 6
Output: true
Explanation: The subarray [2, 4] has sum 6, and 6 is a multiple of 6.
Example 2
Input: nums = [23, 2, 6, 4, 7], k = 13
Output: false
Explanation: There is no subarray of size at least 2 whose sum is a multiple of 13.
Brute Force Approach
The most direct method generates every contiguous subarray containing at least two elements.
For every selected range, the complete sum is calculated separately. The method returns true as soon as a range has a sum divisible by k.
Algorithm
Store the array size in
n. Ifn < 2, returnfalsebecause a valid subarray must contain at least two elements.Treat every index
startas a possible beginning and every indexendfromstart + 1ton - 1as an ending position. Starting fromstart + 1guarantees that the selected range contains at least two elements.For every range
[start, end], initializecurrentSumwith0and add all elements fromstartthroughendto calculate its complete sum.If
k != 0, check whethercurrentSum % k == 0, which means the selected sum is divisible byk.If
k == 0, check whethercurrentSum == 0instead, since modulo by zero cannot be performed.Return
trueimmediately when a valid subarray is found. Returnfalseafter all possible ranges have been examined.
Dry Run
Continuous Subarray Sum Brute Force Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: bool checkSubarraySum(vector<int>& nums, int k) { int n = nums.size(); // At least two elements are required. if (n < 2) { return false; } for (int start = 0; start < n - 1; start++) { for (int end = start + 1; end < n; end++) { long long currentSum = 0; // Calculate the sum of the selected range. for (int index = start; index <= end; index++) { currentSum += nums[index]; } /* * For k = 0, only a zero-sum * subarray satisfies the condition. */ if (k == 0) { if (currentSum == 0) { return true; } } else if (currentSum % k == 0) { return true; } } } return false; }};int main() { vector<int> nums = {23, 2, 4, 6, 7}; int k = 6; Solution solution; cout << (solution.checkSubarraySum(nums, k) ? "true" : "false"); return 0;}Complexity Analysis
Time Complexity: O(N³), where N represents the array size. Two loops select every starting and ending index, while another traversal calculates the sum of each selected range.
Space Complexity: O(1), because only loop indices and currentSum require auxiliary storage.
Better Approach
The Brute Force Approach recalculates every selected range from the beginning.
For one fixed starting index, the next subarray contains the previous range plus one additional element. Maintaining a running sum allows every new range to be checked with one addition.
Algorithm
Store the array size in
n. If fewer than two elements are available, returnfalse.Treat every index
startas the beginning of a new group of subarrays and initializecurrentSumwith0.Move
endfromstartton - 1and addnums[end]tocurrentSum. This extends the previous range by one element instead of recalculating its complete sum.Check the divisibility condition only when
end - start + 1 >= 2, ensuring that single-element ranges are never accepted.If
k != 0, returntruewhencurrentSum % k == 0. Ifk == 0, returntruewhencurrentSum == 0.Return
falseif every possible subarray has been processed without finding a valid one.
Dry Run
Continuous Subarray Sum Better Approach Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: bool checkSubarraySum(vector<int>& nums, int k) { int n = nums.size(); // At least two elements are required. if (n < 2) { return false; } for (int start = 0; start < n - 1; start++) { long long currentSum = 0; for (int end = start; end < n; end++) { currentSum += nums[end]; // Single-element ranges are not valid. if (end - start + 1 < 2) { continue; } /* * For k = 0, only a zero-sum * subarray satisfies the condition. */ if (k == 0) { if (currentSum == 0) { return true; } } else if (currentSum % k == 0) { return true; } } } return false; }};int main() { vector<int> nums = {23, 2, 4, 6, 7}; int k = 6; Solution solution; cout << (solution.checkSubarraySum(nums, k) ? "true" : "false"); return 0;}Complexity Analysis
Time Complexity: O(N²), where N represents the array size. Every starting position extends through all possible ending positions.
Space Complexity: O(1), because only loop indices and currentSum require auxiliary storage.
Optimal Approach
Consider two prefix sums ending at different indices.
When both prefix sums have the same remainder after division by k, subtracting the earlier prefix from the later prefix produces a value divisible by k. Therefore, the elements between those prefix positions form a subarray whose sum is a multiple of k.
Only the first index of each remainder should be stored. The earliest index creates the largest possible distance and provides the best chance of satisfying the required subarray length.
When k = 0, remainders cannot be calculated. In that case, repeated prefix sums indicate that the elements between those positions have a sum of 0.
Algorithm
Store the array size in
n. Ifn < 2, returnfalse.Create a hash map
firstIndexand initialize it with{0: -1}. The index-1represents the empty prefix before the array, allowing subarrays beginning at index0to satisfy the length condition naturally.Initialize
prefixSumwith0and traverse the array from left to right, treating each current index as a possible ending position.Add
nums[index]toprefixSum. Ifk != 0, use the normalized remainder ofprefixSumby|k|as the key. Ifk == 0, useprefixSumitself because equal prefix sums indicate a zero-sum subarray.If the key has appeared before, check whether
index - firstIndex[key] >= 2. A distance of at least2means the elements between the two prefix positions form a valid subarray.If the key has not appeared before, store its current index. Keeping only its earliest occurrence gives future indices the largest possible distance. Return
falseif the traversal ends without finding a valid range.
Dry Run
Continuous Subarray Sum Optimal Approach Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: bool checkSubarraySum(vector<int>& nums, int k) { int n = nums.size(); // At least two elements are required. if (n < 2) { return false; } unordered_map<long long, int> firstIndex; /* * The empty prefix starts at index -1, * allowing ranges beginning at index 0. */ firstIndex[0] = -1; long long prefixSum = 0; long long divisor = llabs((long long)k); for (int index = 0; index < n; index++) { prefixSum += nums[index]; long long key; if (k == 0) { // Equal prefix sums produce a zero-sum range. key = prefixSum; } else { /* * Normalize the remainder so equivalent * values use the same hash-map key. */ key = ((prefixSum % divisor) + divisor) % divisor; } if (firstIndex.find(key) != firstIndex.end()) { /* * A distance of at least two gives * a valid subarray length. */ if (index - firstIndex[key] >= 2) { return true; } } else { // Keep the earliest position for this key. firstIndex[key] = index; } } return false; }};int main() { vector<int> nums = {23, 2, 4, 6, 7}; int k = 6; Solution solution; cout << (solution.checkSubarraySum(nums, k) ? "true" : "false"); return 0;}Complexity Analysis
Time Complexity: O(N) on average, where N represents the array size. Every element requires one hash-map lookup and, when necessary, one insertion.
Space Complexity: O(N), because the hash map may store up to N + 1 distinct remainder or prefix-sum values.
FAQs
Q1. Why do equal prefix remainders produce a sum divisible by k?
If two prefix sums have the same remainder, their difference has remainder 0. That difference represents the sum of the elements between the two prefix positions.
Q2. Why is remainder 0 initially stored at index -1?
Index -1 represents the empty prefix before the array starts. This allows a valid subarray beginning at index 0 to satisfy the length check naturally.
Q3. Why is only the first index of each remainder stored?
The earliest occurrence produces the greatest distance from future positions. Replacing it with a later index could hide a valid subarray of length at least 2.
Q4. Why must negative remainders be normalized?
Some programming languages produce negative modulo results for negative prefix sums. Normalization ensures that equivalent remainder classes use the same hash-map key.
Q5. How should k = 0 be handled?
Modulo cannot be performed with zero. A repeated prefix sum must be searched instead, because equal prefix sums mean the subarray between them has sum 0.
Q6. Can a sliding-window approach solve this problem?
A standard sliding window is not reliable when negative values are allowed. Adding or removing an element does not change the sum in a predictable direction.
Be the first to add a comment.