LeetCode 2941 - Maximum GCD-Sum of a Subarray
The problem asks us to find the maximum gcd-sum of a subarray of a given integer array nums with the constraint that the subarray has at least k elements.
Difficulty: 🔴 Hard
Topics: Array, Math, Binary Search, Number Theory
Solution
Problem Understanding
The problem asks us to find the maximum gcd-sum of a subarray of a given integer array nums with the constraint that the subarray has at least k elements. The gcd-sum of a subarray is defined as the product of the sum of its elements and the greatest common divisor (GCD) of its elements. In other words, if a subarray a has elements [a_1, a_2, ..., a_m], then its gcd-sum is:
gcd_sum(a) = (a_1 + a_2 + ... + a_m) * gcd(a_1, a_2, ..., a_m)
The input consists of an array nums of length up to 10^5 and integer values up to 10^6, so naive solutions that consider all subarrays will be too slow. The output should be a single integer representing the maximum gcd-sum among all valid subarrays of length at least k.
Key observations include recognizing that subarrays must be contiguous, and that the GCD function has the property that for any subarray, gcd(a, b, c) <= min(a, b, c). This observation allows us to reason about potential values of GCD in an efficient way.
Edge cases to consider are subarrays where all elements are the same, where k = 1 (single elements are allowed), and where elements are prime or coprime with each other, which affects the GCD computation.
Approaches
Brute Force
The brute-force approach is straightforward but inefficient. We can iterate over all possible subarrays of length at least k, compute their sum and GCD, and then calculate the gcd-sum. This guarantees correctness because it evaluates all possible subarrays. However, the complexity is prohibitive because there are O(n^2) subarrays and computing the GCD of each subarray takes O(m) for a subarray of length m. Therefore, the total time complexity is roughly O(n^3), which is unacceptable for n = 10^5.
Optimal Approach
The key insight for a faster solution is to fix a candidate GCD and then find the maximum sum of a contiguous subarray whose elements are divisible by that GCD. This converts the problem into a variant of the maximum subarray sum problem with an additional divisibility constraint.
Steps to optimize:
- Precompute all divisors for each number up to 10^6.
- Iterate over candidate GCDs in decreasing order. For each GCD
g, transform the array into values divisible byg(keep as is) or non-divisible (set to 0 or ignore). - Use a sliding window or prefix sum approach to find the maximum sum of contiguous subarrays of length at least
kwhere all elements are divisible byg. - Multiply the candidate GCD
gwith the maximum subarray sum to get the gcd-sum for that candidate. - Track the maximum across all candidate GCDs.
This approach reduces the problem from considering all subarrays and GCDs independently to iterating over potential GCDs (up to 10^6) and then performing a linear scan per candidate.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^3) | O(1) | Compute sum and GCD for all subarrays of length >= k |
| Optimal | O(n * log(max_num) * num_divisors) | O(n) | Iterate over candidate GCDs and use sliding window for max sum |
Algorithm Walkthrough
-
Identify the maximum value in the array
numsto limit candidate GCDs. -
Initialize a variable
max_gcd_sumto 0. -
For each possible GCD
gfrom 1 to max value: -
Transform the array such that each element is either its value if divisible by
gor 0 if not. -
Use a sliding window to find the maximum sum of contiguous elements of length at least
kthat are divisible byg. -
Compute the gcd-sum as
g * max_sum. -
Update
max_gcd_sumif this gcd-sum is larger. -
Return
max_gcd_sum.
Why it works: By iterating over all possible GCDs and using a sliding window on valid subarrays, we ensure that every potential gcd-sum is considered. Since larger GCDs are considered first, the algorithm finds the optimal gcd-sum efficiently.
Python Solution
from math import gcd
from typing import List
class Solution:
def maxGcdSum(self, nums: List[int], k: int) -> int:
max_val = max(nums)
max_gcd_sum = 0
# Iterate over possible gcd candidates
for g in range(1, max_val + 1):
current_sum = 0
best_sum = 0
window = []
for num in nums:
if num % g == 0:
window.append(num)
current_sum += num
if len(window) > k:
current_sum -= window.pop(0)
if len(window) >= k:
best_sum = max(best_sum, current_sum)
else:
window.clear()
current_sum = 0
max_gcd_sum = max(max_gcd_sum, best_sum * g)
return max_gcd_sum
The Python implementation iterates over all candidate GCDs. For each GCD, it uses a sliding window approach to find contiguous subarrays of length at least k that are divisible by the candidate GCD. It tracks the maximum subarray sum and updates the overall maximum gcd-sum.
Go Solution
func maxGcdSum(nums []int, k int) int64 {
maxVal := 0
for _, num := range nums {
if num > maxVal {
maxVal = num
}
}
var maxGcdSum int64 = 0
for g := 1; g <= maxVal; g++ {
currentSum := 0
window := []int{}
for _, num := range nums {
if num%g == 0 {
window = append(window, num)
currentSum += num
if len(window) > k {
currentSum -= window[0]
window = window[1:]
}
if len(window) >= k {
candidate := int64(currentSum) * int64(g)
if candidate > maxGcdSum {
maxGcdSum = candidate
}
}
} else {
window = []int{}
currentSum = 0
}
}
}
return maxGcdSum
}
The Go version mirrors the Python approach. The main difference is explicit handling of slices, where removing the first element requires slice reassignment. Type casting is necessary to avoid integer overflow for large sums.
Worked Examples
Example 1
nums = [2,1,4,4,4,2], k = 2
-
Maximum value is 4, candidate GCDs: 1,2,3,4
-
Candidate GCD = 4:
-
Transformed array: [0,0,4,4,4,0]
-
Sliding window of size >= 2: [4,4,4] sum = 12
-
GCD sum = 12 * 4 = 48
-
No other GCD yields a higher sum
-
Output = 48
Example 2
nums = [7,3,9,4], k = 1
-
Maximum value is 9
-
Candidate GCD = 9:
-
Transformed array: [0,0,9,0]
-
Sliding window of size >=1: [9] sum = 9
-
GCD sum = 9*9=81
-
Output = 81
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n * max(nums)) | Iterate over all GCD candidates and perform linear scan of nums |
| Space | O(n) | Sliding window to track sum of contiguous elements |
The reasoning is that we iterate over all GCD candidates up to the maximum value in nums, and for each candidate, we scan the array once. The space is proportional to the size of the sliding window.
Test Cases
# Provided examples
assert Solution().maxGcdSum([2,1,4,4,4,2], 2) == 48 # Example 1
assert Solution().maxGcdSum([7,3,9,4], 1) == 81 # Example 2
# Edge cases
assert Solution().maxGcdSum([1,1,1,1,1], 1) == 5 # All ones
assert Solution().maxGcdSum([6,12,18], 2) == 72 # All multiples of 6
assert Solution().maxGcdSum([2,3,5,7], 1) == 7 # Single max element
assert Solution().maxGcdSum([2], 1) == 4 # Single element array
| Test | Why |
|---|---|
| [2,1,4,4,4,2], 2 |