LeetCode 3487 - Maximum Unique Subarray Sum After Deletion
The problem asks us to find the maximum sum of a subarray from an integer array nums under two key constraints: all elements in the chosen subarray must be unique, and we are allowed to delete any number of elements (except leaving the array empty) before selecting the subarray.
Difficulty: 🟢 Easy
Topics: Array, Hash Table, Greedy
Solution
Problem Understanding
The problem asks us to find the maximum sum of a subarray from an integer array nums under two key constraints: all elements in the chosen subarray must be unique, and we are allowed to delete any number of elements (except leaving the array empty) before selecting the subarray. In other words, we are looking for the largest sum achievable by any subarray that contains no repeated elements.
The input nums is a list of integers ranging from -100 to 100, and its length is up to 100. The output is a single integer representing the maximum sum of a subarray that satisfies the uniqueness constraint. The constraints are small, so even an O(n^2) solution could work, but there is a more elegant O(n) approach using a sliding window.
Important edge cases include arrays with all identical numbers, arrays containing negative numbers, arrays with a mix of positive and negative numbers, and arrays of length 1. The problem guarantees that the array is non-empty, which means we do not have to handle empty inputs.
Approaches
Brute Force
A brute-force approach would involve generating all possible subarrays of nums and checking each subarray for uniqueness. For each unique subarray, we calculate the sum and keep track of the maximum. This approach is correct because it explores all possibilities, but it is inefficient. There are O(n^2) possible subarrays, and checking uniqueness in each subarray can take up to O(n), leading to an O(n^3) time complexity. This is unnecessary for the given constraints and can be optimized.
Optimal Approach
The key observation is that we need a contiguous subarray with unique elements, which naturally lends itself to a sliding window approach. We can maintain a window of unique elements using a hash set. As we iterate through the array, we expand the window by including new elements. If we encounter a duplicate, we shrink the window from the left until the duplicate is removed. During this process, we maintain the sum of the current window and update the maximum sum encountered. This approach works in linear time O(n) because each element is added and removed at most once from the window.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^3) | O(n) | Generate all subarrays and check uniqueness |
| Optimal | O(n) | O(n) | Sliding window with hash set to maintain uniqueness |
Algorithm Walkthrough
-
Initialize a hash set
seento store the unique elements in the current window. -
Initialize two pointers
leftandrightat the start of the array. These will define the current window. -
Initialize
current_sumto 0 andmax_sumto negative infinity (or 0 if we know sums are non-negative). -
Iterate through
numswithrightpointer. For eachnums[right]: -
While
nums[right]is already inseen, removenums[left]fromseenand subtractnums[left]fromcurrent_sum. Incrementleftto shrink the window. -
Add
nums[right]toseenand addnums[right]tocurrent_sum. -
Update
max_sumas the maximum ofmax_sumandcurrent_sum. -
After the iteration completes, return
max_sum.
Why it works: The sliding window maintains the invariant that all elements between left and right are unique. By expanding the window whenever possible and shrinking it when duplicates are found, we consider all valid subarrays without redundant checks. Maintaining a running sum ensures we can calculate the sum in O(1) for each step.
Python Solution
from typing import List
class Solution:
def maxSum(self, nums: List[int]) -> int:
seen = set()
left = 0
current_sum = 0
max_sum = float('-inf')
for right in range(len(nums)):
while nums[right] in seen:
seen.remove(nums[left])
current_sum -= nums[left]
left += 1
seen.add(nums[right])
current_sum += nums[right]
max_sum = max(max_sum, current_sum)
return max_sum
The Python solution initializes a hash set seen to track unique elements in the current window. The left pointer marks the start of the window, and current_sum tracks the sum of elements in the window. As we iterate with right, we remove elements from the left until the current element can be added uniquely. We then update the maximum sum. This closely follows the algorithm steps described above.
Go Solution
func maxSum(nums []int) int {
seen := make(map[int]bool)
left := 0
currentSum := 0
maxSum := -1 << 31
for right := 0; right < len(nums); right++ {
for seen[nums[right]] {
delete(seen, nums[left])
currentSum -= nums[left]
left++
}
seen[nums[right]] = true
currentSum += nums[right]
if currentSum > maxSum {
maxSum = currentSum
}
}
return maxSum
}
In Go, we use a map to track elements in the window because Go does not have a built-in set type. The logic mirrors the Python version. The initial maxSum is set to the minimum integer to handle arrays with all negative numbers. Deletion from the map and updating currentSum maintain the sliding window invariant.
Worked Examples
Example 1: nums = [1,2,3,4,5]
| right | nums[right] | seen | current_sum | max_sum |
|---|---|---|---|---|
| 0 | 1 | {1} | 1 | 1 |
| 1 | 2 | {1,2} | 3 | 3 |
| 2 | 3 | {1,2,3} | 6 | 6 |
| 3 | 4 | {1,2,3,4} | 10 | 10 |
| 4 | 5 | {1,2,3,4,5} | 15 | 15 |
Return 15.
Example 2: nums = [1,1,0,1,1]
| right | nums[right] | seen | current_sum | max_sum |
|---|---|---|---|---|
| 0 | 1 | {1} | 1 | 1 |
| 1 | 1 | {} → {1} | 0 → 1 | 1 |
| 2 | 0 | {1,0} | 1 | 1 |
| 3 | 1 | {0,1} | 1 | 1 |
| 4 | 1 | {1} | 1 | 1 |
Return 1.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each element is added and removed from the window at most once |
| Space | O(n) | Hash set or map stores unique elements in the current window |
Because the algorithm iterates through nums once, and every element is processed at most twice, the solution is linear. The hash set or map requires additional space proportional to the number of unique elements in the array.
Test Cases
# Provided examples
assert Solution().maxSum([1,2,3,4,5]) == 15 # all unique, sum entire array
assert Solution().maxSum([1,1,0,1,1]) == 1 # duplicates, only single unique elements can be used
assert Solution().maxSum([1,2,-1,-2,1,0,-1]) == 3 # mix of positive and negative
# Boundary tests
assert Solution().maxSum([100]) == 100 # single element
assert Solution().maxSum([-100]) == -100 # single negative element
assert Solution().maxSum([1]*100) == 1 # all elements identical
assert Solution().maxSum(list(range(1,101))) == 5050 # increasing sequence 1..100
# Stress tests
assert Solution().maxSum([0]*50 + list(range(1,51))) == sum(range(1,51)) # zeros and unique positives
assert Solution().maxSum([-1,-2,-3,-4,-5]) == -1 # all negative elements
| Test | Why |
|---|---|
[1,2,3,4,5] |
Array entirely unique |
[1,1,0,1,1] |
Array with repeated duplicates |
[1,2,-1,-2,1,0,-1] |
Array with mix of positive and negative numbers |
[100] |
Single element array |
[-100] |
Single negative element |
[1]*100 |
All elements identical |
list(range(1,101)) |
Maximum length with all unique elements |
[0]*50 + list(range(1,51)) |
Zeros and positives combined |
| `[-1 |