2607. Make K-Subarray Sums Equal
Problem Description
You have a circular array arr
where the first element follows the last element (wrapping around). You need to make all subarrays of length k
have equal sums.
Key Points:
-
The array is 0-indexed and circular, meaning:
- After the last element, we wrap back to the first element
- For example, if
arr = [1, 2, 3]
andk = 2
, the subarrays are:[1,2]
,[2,3]
, and[3,1]
(wrapping around)
-
You can perform operations to modify the array:
- Pick any element and increase or decrease it by 1
- Each increase or decrease counts as one operation
- You can do this any number of times
-
Your goal is to make all subarrays of length
k
have the same sum using the minimum number of operations.
Example Understanding:
If we have arr = [1, 4, 1, 3]
and k = 2
:
- The subarrays of length 2 are:
[1,4]
,[4,1]
,[1,3]
,[3,1]
- Their initial sums are: 5, 5, 4, 4
- We need to modify elements so all these sums become equal
The challenge is finding which values to change the array elements to, such that:
- All k-length subarrays have equal sums
- The total number of operations (changes) is minimized
The solution leverages the mathematical relationship between array positions that must be equal due to the circular subarray sum constraint, using GCD to identify these groups of positions.
Intuition
Let's think about what happens when all k-length subarrays have equal sums.
Consider subarrays starting at positions i
and i+1
:
- Subarray at position
i
:arr[i] + arr[i+1] + ... + arr[i+k-1]
- Subarray at position
i+1
:arr[i+1] + arr[i+2] + ... + arr[i+k]
For these two sums to be equal:
arr[i] + arr[i+1] + ... + arr[i+k-1] = arr[i+1] + arr[i+2] + ... + arr[i+k]
Simplifying by canceling common terms:
arr[i] = arr[i+k]
This means elements that are k positions apart must be equal!
Since the array is circular with length n
, we get:
arr[0] = arr[k] = arr[2k] = arr[3k] = ...
(all modulo n)arr[1] = arr[1+k] = arr[1+2k] = arr[1+3k] = ...
(all modulo n)- And so on...
This creates groups of positions that must have the same value. The number of such groups is actually gcd(n, k)
. Why? Because the pattern of which positions belong together repeats based on the greatest common divisor of n
and k
.
For example, if n = 6
and k = 4
:
gcd(6, 4) = 2
- Group 1: positions
{0, 4, 2}
(following 0 β 4 β 2 β 0) - Group 2: positions
{1, 5, 3}
(following 1 β 5 β 3 β 1)
For each group of positions that must be equal, we need to change all values to some target value. The optimal target is the median of the current values in that group, as the median minimizes the sum of absolute deviations.
Therefore, the solution:
- Identifies
gcd(n, k)
groups - For each group, collects all values at positions
i, i+g, i+2g, ...
whereg = gcd(n, k)
- Finds the median of each group
- Calculates the total cost as the sum of distances from each element to its group's median
Solution Approach
The implementation follows the mathematical insight that elements at positions differing by k
must be equal.
Step 1: Calculate the GCD
g = gcd(n, k)
This determines how many independent groups of positions exist. Each group contains positions that must have the same value.
Step 2: Process Each Group
for i in range(g):
We iterate through g
groups, where each group starts at position i
.
Step 3: Collect Elements in Each Group
t = sorted(arr[i:n:g])
For group i
, we collect elements at positions i
, i+g
, i+2g
, ..., up to n-1
. The slice arr[i:n:g]
achieves this by:
- Starting at index
i
- Going up to index
n
- Taking every
g
-th element
We sort these values to easily find the median.
Step 4: Find the Median
mid = t[len(t) >> 1]
The median is found at the middle position of the sorted array. The expression len(t) >> 1
is equivalent to len(t) // 2
(right shift by 1 divides by 2).
Step 5: Calculate Operations for This Group
ans += sum(abs(x - mid) for x in t)
For each element in the group, we calculate how many operations are needed to change it to the median value. This is simply the absolute difference |x - mid|
. We sum these differences for all elements in the group.
Step 6: Return Total Operations
After processing all g
groups, ans
contains the total minimum number of operations needed.
Example Walkthrough:
If arr = [1, 4, 1, 3]
and k = 2
:
n = 4
,k = 2
, sog = gcd(4, 2) = 2
- Group 0: positions {0, 2} β values {1, 1} β median = 1 β cost = 0
- Group 1: positions {1, 3} β values {4, 3} β median = 3 or 4 β cost = 1
- Total operations = 1
The algorithm efficiently identifies which positions must be equal and optimally adjusts them to their group's median value, minimizing the total operations required.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with arr = [2, 5, 3, 7, 1, 6]
and k = 4
.
Step 1: Identify the groups
n = 6
,k = 4
g = gcd(6, 4) = 2
- So we have 2 groups of positions that must be equal
Step 2: Determine which positions belong to each group
For circular subarrays of length 4 to have equal sums, elements k positions apart must be equal:
- Position 0 must equal position 4 (since 0+4=4)
- Position 1 must equal position 5 (since 1+4=5)
- Position 2 must equal position 0 (since 2+4=6, and 6 mod 6 = 0)
- Position 3 must equal position 1 (since 3+4=7, and 7 mod 6 = 1)
- Position 4 must equal position 2 (since 4+4=8, and 8 mod 6 = 2)
- Position 5 must equal position 3 (since 5+4=9, and 9 mod 6 = 3)
This creates our groups:
- Group 0: positions {0, 2, 4} with values {2, 3, 1}
- Group 1: positions {1, 3, 5} with values {5, 7, 6}
Step 3: Find the median for each group
Group 0:
- Values: {2, 3, 1}
- Sorted: {1, 2, 3}
- Median: 2 (middle element)
Group 1:
- Values: {5, 7, 6}
- Sorted: {5, 6, 7}
- Median: 6 (middle element)
Step 4: Calculate operations needed
Group 0 (target value = 2):
- Position 0: |2 - 2| = 0 operations
- Position 2: |3 - 2| = 1 operation
- Position 4: |1 - 2| = 1 operation
- Subtotal: 2 operations
Group 1 (target value = 6):
- Position 1: |5 - 6| = 1 operation
- Position 3: |7 - 6| = 1 operation
- Position 5: |6 - 6| = 0 operations
- Subtotal: 2 operations
Total minimum operations: 4
Verification:
After modifications, the array becomes [2, 6, 2, 6, 2, 6]
.
All subarrays of length 4:
- [2, 6, 2, 6] β sum = 16
- [6, 2, 6, 2] β sum = 16
- [2, 6, 2, 6] β sum = 16
- [6, 2, 6, 2] β sum = 16
- [2, 6, 2, 6] β sum = 16
- [6, 2, 6, 2] β sum = 16
All sums are equal! β
Solution Implementation
1from typing import List
2from math import gcd
3
4class Solution:
5 def makeSubKSumEqual(self, arr: List[int], k: int) -> int:
6 """
7 Find minimum operations to make all k-length subarrays have equal sum.
8 Each operation allows changing one element to any value.
9
10 Key insight: Elements at positions i and (i+k) mod n must be equal
11 for all k-length subarrays to have the same sum.
12 This creates GCD(n, k) independent groups.
13 """
14 n = len(arr)
15
16 # Find the number of independent groups
17 # Elements at positions i, i+g, i+2g, ... must all be equal
18 group_size = gcd(n, k)
19
20 total_operations = 0
21
22 # Process each group independently
23 for group_start in range(group_size):
24 # Extract all elements belonging to this group
25 # Group contains elements at indices: group_start, group_start + group_size, ...
26 group_elements = sorted(arr[group_start:n:group_size])
27
28 # Find the median of the group (optimal target value)
29 # Using bit shift for integer division by 2
30 median_index = len(group_elements) >> 1
31 median_value = group_elements[median_index]
32
33 # Calculate operations needed to change all elements to the median
34 operations_for_group = sum(abs(element - median_value) for element in group_elements)
35 total_operations += operations_for_group
36
37 return total_operations
38
1class Solution {
2 public long makeSubKSumEqual(int[] arr, int k) {
3 int n = arr.length;
4 // Calculate GCD to determine the number of independent groups
5 // Elements at positions differing by gcd(n, k) must be equal
6 int gcdValue = gcd(n, k);
7 long totalOperations = 0;
8
9 // Process each group of related positions
10 for (int groupIndex = 0; groupIndex < gcdValue; ++groupIndex) {
11 // Collect all values that must be made equal in this group
12 List<Integer> groupValues = new ArrayList<>();
13
14 // Add elements at positions: groupIndex, groupIndex + gcdValue, groupIndex + 2*gcdValue, ...
15 for (int position = groupIndex; position < n; position += gcdValue) {
16 groupValues.add(arr[position]);
17 }
18
19 // Sort values to find the median
20 groupValues.sort((a, b) -> a - b);
21
22 // Get the median value (optimal target for minimizing total operations)
23 int medianValue = groupValues.get(groupValues.size() >> 1);
24
25 // Calculate total operations needed to change all values to median
26 for (int value : groupValues) {
27 totalOperations += Math.abs(value - medianValue);
28 }
29 }
30
31 return totalOperations;
32 }
33
34 /**
35 * Calculate Greatest Common Divisor using Euclidean algorithm
36 * @param a first number
37 * @param b second number
38 * @return GCD of a and b
39 */
40 private int gcd(int a, int b) {
41 return b == 0 ? a : gcd(b, a % b);
42 }
43}
44
1class Solution {
2public:
3 long long makeSubKSumEqual(vector<int>& arr, int k) {
4 int arraySize = arr.size();
5
6 // Find GCD to determine the number of independent groups
7 // Elements at positions i, i+g, i+2g, ... must be equal for all k-length subarrays to have equal sum
8 int groupCount = gcd(arraySize, k);
9
10 long long totalOperations = 0;
11
12 // Process each independent group
13 for (int groupIndex = 0; groupIndex < groupCount; ++groupIndex) {
14 // Collect all elements belonging to current group
15 // These elements are at positions: groupIndex, groupIndex + groupCount, groupIndex + 2*groupCount, ...
16 vector<int> groupElements;
17 for (int position = groupIndex; position < arraySize; position += groupCount) {
18 groupElements.push_back(arr[position]);
19 }
20
21 // Sort to find the median efficiently
22 sort(groupElements.begin(), groupElements.end());
23
24 // Use median as target value (minimizes sum of absolute differences)
25 int medianValue = groupElements[groupElements.size() / 2];
26
27 // Calculate total operations needed to make all elements in this group equal to median
28 for (int element : groupElements) {
29 totalOperations += abs(element - medianValue);
30 }
31 }
32
33 return totalOperations;
34 }
35};
36
1/**
2 * Makes all subarrays of length k have equal sum by modifying array elements
3 * with minimum total operations
4 * @param arr - The input array to modify
5 * @param k - The length of subarrays that should have equal sum
6 * @returns The minimum number of operations needed
7 */
8function makeSubKSumEqual(arr: number[], k: number): number {
9 const arrayLength: number = arr.length;
10
11 // Find GCD to determine the number of independent groups
12 // Elements at positions differing by GCD(n, k) must be equal
13 const groupCount: number = gcd(arrayLength, k);
14
15 let totalOperations: number = 0;
16
17 // Process each group independently
18 for (let groupIndex = 0; groupIndex < groupCount; groupIndex++) {
19 const groupElements: number[] = [];
20
21 // Collect all elements belonging to current group
22 // Elements at positions i, i+g, i+2g, ... belong to same group
23 for (let position = groupIndex; position < arrayLength; position += groupCount) {
24 groupElements.push(arr[position]);
25 }
26
27 // Sort elements to find median
28 groupElements.sort((a: number, b: number) => a - b);
29
30 // Use median as target value (minimizes sum of absolute differences)
31 const medianIndex: number = groupElements.length >> 1;
32 const medianValue: number = groupElements[medianIndex];
33
34 // Calculate total operations needed to make all elements equal to median
35 for (const element of groupElements) {
36 totalOperations += Math.abs(element - medianValue);
37 }
38 }
39
40 return totalOperations;
41}
42
43/**
44 * Calculates the greatest common divisor of two numbers using Euclidean algorithm
45 * @param a - First number
46 * @param b - Second number
47 * @returns The greatest common divisor of a and b
48 */
49function gcd(a: number, b: number): number {
50 // Base case: when b is 0, GCD is a
51 if (b === 0) {
52 return a;
53 }
54
55 // Recursive case: GCD(a, b) = GCD(b, a mod b)
56 return gcd(b, a % b);
57}
58
Time and Space Complexity
Time Complexity: O(n * log(n/g))
where n
is the length of the array and g = gcd(n, k)
- Computing
gcd(n, k)
takesO(log(min(n, k)))
time - The outer loop runs
g
times - For each iteration
i
, we extract elements at indicesi, i+g, i+2g, ...
which gives usn/g
elements - Sorting these
n/g
elements takesO((n/g) * log(n/g))
time - Finding the median takes
O(1)
time - Computing the sum of absolute differences takes
O(n/g)
time - Total time for all iterations:
g * O((n/g) * log(n/g)) = O(n * log(n/g))
- Overall:
O(log(min(n, k)) + n * log(n/g))
=O(n * log(n/g))
Space Complexity: O(n/g)
- The sorted array
t
in each iteration contains at mostn/g
elements - The slicing operation
arr[i:n:g]
creates a new list withn/g
elements - No other significant auxiliary space is used
- Therefore, the space complexity is
O(n/g)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Circular Nature of the Array
Pitfall: Developers often forget that the array is circular and only consider consecutive subarrays without wrapping. For example, with arr = [1, 2, 3]
and k = 2
, they might only consider [1,2]
and [2,3]
, missing the wrapped subarray [3,1]
.
Solution: Always remember that for a circular array of length n
, there are exactly n
subarrays of length k
, each starting at positions 0, 1, 2, ..., n-1
and wrapping around when necessary.
2. Incorrect Group Identification
Pitfall: Attempting to manually identify which positions must be equal without using the GCD relationship. Some might think elements at positions i
and i+k
must be equal, but miss that this creates a chain of dependencies that ultimately forms gcd(n,k)
groups.
Solution: Use the mathematical property that the number of independent groups equals gcd(n, k)
. Elements at positions differing by multiples of gcd(n, k)
must be equal.
3. Using Mean Instead of Median
Pitfall: Using the average (mean) value of a group as the target instead of the median:
# Wrong approach
target = sum(group_elements) / len(group_elements)
operations = sum(abs(x - target) for x in group_elements)
Solution: Always use the median for minimizing the sum of absolute deviations. The median is the optimal choice when the cost function is the L1 norm (sum of absolute differences).
4. Off-by-One Error in Median Calculation
Pitfall: For even-length arrays, incorrectly handling median selection. Some implementations might use:
# Potentially wrong for even-length arrays
median = group_elements[len(group_elements) // 2 - 1]
Solution: For this problem, when the group has even length, either middle element works as the median (both give the same total cost). The code correctly uses len(group_elements) >> 1
which gives the upper middle element.
5. Modifying the Original Array During Processing
Pitfall: Changing array values while iterating through groups, which affects subsequent group calculations:
# Wrong: modifying arr while processing
for i in range(g):
group = arr[i:n:g]
median = sorted(group)[len(group) // 2]
for j in range(i, n, g):
arr[j] = median # Don't do this!
Solution: Calculate the cost without modifying the original array. The problem only asks for the number of operations, not the final array state.
What data structure does Breadth-first search typically uses to store intermediate states?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Want a Structured Path to Master System Design Too? Donβt Miss This!