Facebook Pixel

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:

  1. 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] and k = 2, the subarrays are: [1,2], [2,3], and [3,1] (wrapping around)
  2. 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
  3. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Identifies gcd(n, k) groups
  2. For each group, collects all values at positions i, i+g, i+2g, ... where g = gcd(n, k)
  3. Finds the median of each group
  4. Calculates the total cost as the sum of distances from each element to its group's median

Learn more about Greedy, Math and Sorting patterns.

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, so g = 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 Evaluator

Example 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) takes O(log(min(n, k))) time
  • The outer loop runs g times
  • For each iteration i, we extract elements at indices i, i+g, i+2g, ... which gives us n/g elements
  • Sorting these n/g elements takes O((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 most n/g elements
  • The slicing operation arr[i:n:g] creates a new list with n/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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What data structure does Breadth-first search typically uses to store intermediate states?


Recommended Readings

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

Load More