Given an integer array nums and an integer k, find three non-overlapping subarrays of length k with maximum sum and return them.
Return the result as a list of indices representing the starting position of each interval (0-indexed). If there are multiple answers, return the lexicographically smallest one.
solutions
top-down recursion w/ memoization
Immediately, the intuition should be that we can just simply try to use DFS to explore the possibility space, choosing to start a subarray at an index, or skip the index.
# O(k*2^n) - 2 branches n times, O(k) sum operation per node in DFS
# With memoization, there are O(3n) ~ O(n) unique states, so O(nk)
def maxSumOfThreeSubarrays(self, nums: List[int], k: int) -> List[int]:
memo = {}
def recurse(idx, completed):
if completed == 3:
return 0, []
# break early if not enough values
# for remaining subarrays
remaining_vals = len(nums)-idx
if (3-completed)*k > remaining_vals:
return 0, []
if (idx, completed) in memo:
return memo[(idx, completed)][0], memo[(idx, completed)][1]
# start next subarray at current index
# take k values
subsum = sum(nums[idx:idx+k])
rest_sum, rest_indices = recurse(idx+k, completed+1)
# skip this index
skip_sum, skip_indices = recurse(idx+1, completed)
total, indices = None, None
# >= because prefer take over skip
# (gives lexicographically lower answer)
if subsum+rest_sum >= skip_sum:
total, indices = subsum+rest_sum, [idx]+rest_indices
else:
total, indices = skip_sum, skip_indices
memo[(idx, completed)] = [total, indices]
return total, indices
_, start_indices = recurse(0, 0)
return start_indicesWe can then add prefix sums to remove the operation in the recursive step, improving our runtime to .
def maxSumOfThreeSubarrays(self, nums: List[int], k: int) -> List[int]:
prefix_sums = [0]
for num in nums:
prefix_sums.append(prefix_sums[-1]+num)
memo = {}
def recurse(idx, completed):
if completed == 3:
return 0, []
# break early if not enough values
# for remaining subarrays
remaining_vals = len(nums)-idx
if (3-completed)*k > remaining_vals:
return 0, []
if (idx, completed) in memo:
return memo[(idx, completed)][0], memo[(idx, completed)][1]
# start next subarray at current index
# take k values
subsum = prefix_sums[idx+k]-prefix_sums[idx]
rest_sum, rest_indices = recurse(idx+k, completed+1)
# skip this index
skip_sum, skip_indices = recurse(idx+1, completed)
total, indices = None, None
# >= because prefer take over skip
# (gives lexicographically lower answer)
if subsum+rest_sum >= skip_sum:
total, indices = subsum+rest_sum, [idx]+rest_indices
else:
total, indices = skip_sum, skip_indices
memo[(idx, completed)] = [total, indices]
return total, indices
_, start_indices = recurse(0, 0)
return start_indices