1695. Maximum Erasure Value
Problem Description
You have an array of positive integers nums
. Your task is to find and erase a subarray that contains only unique elements (no duplicates). When you erase a subarray, you get a score equal to the sum of all elements in that subarray.
A subarray is a contiguous portion of the original array - it must be a sequence of consecutive elements like a[l], a[l+1], ..., a[r]
for some indices l
and r
.
The goal is to find the maximum possible score you can achieve by erasing exactly one such subarray where all elements are distinct.
For example:
- If
nums = [4,2,4,5,6]
, one valid subarray with unique elements is[2,4,5,6]
with sum = 17 - Another valid subarray is
[4,2]
with sum = 6 - The subarray
[4,2,4]
is NOT valid because it contains duplicate 4s
You need to return the maximum sum among all possible subarrays that contain only unique elements.
Intuition
The key insight is that we want to find the maximum sum of a contiguous subarray with all unique elements. This is essentially a sliding window problem, but we need to handle the constraint of uniqueness.
Think of it this way: as we scan through the array from left to right, we can maintain a "valid window" that contains only unique elements. Whenever we encounter a duplicate, we need to shrink our window from the left until the duplicate is removed.
For example, if we have [4, 2, 4, 5, 6]
and we're at the second 4
(index 2), we know that any subarray containing both 4
s is invalid. So the earliest valid starting point for our current window must be after the first 4
(index 0).
To efficiently track this, we can record the last occurrence position of each number. When we see a number v
at position i
, if we've seen v
before at position d[v]
, then our valid window can only start from position d[v] + 1
or later.
The prefix sum technique comes into play for calculating subarray sums quickly. Instead of recalculating the sum each time, we can use s[i] - s[j]
to get the sum of elements from index j
to i-1
in constant time.
The variable j
acts as a "barrier" - it represents the leftmost position where our current valid window can start. As we move through the array, j
only moves rightward (or stays the same) when we encounter duplicates, ensuring we always maintain a valid window with unique elements.
At each position, we calculate the sum of the current valid window and keep track of the maximum sum seen so far.
Learn more about Sliding Window patterns.
Solution Approach
The solution uses a combination of hash table/array for tracking positions and prefix sum for efficient sum calculation.
Data Structures Used:
d
: An array (or hash table) whered[v]
stores the last occurrence index of valuev
. We initialize it with sizemax(nums) + 1
for direct indexing.s
: A prefix sum array wheres[i]
represents the sum of all elements from index 0 to i-1. We useaccumulate(nums, initial=0)
to build this.j
: A pointer tracking the leftmost valid starting position of our current window.ans
: Stores the maximum sum found so far.
Algorithm Steps:
-
Initialize data structures:
- Create array
d
with zeros, sized to accommodate the maximum value innums
- Build prefix sum array
s
wheres[0] = 0
ands[i] = s[i-1] + nums[i-1]
- Set
ans = 0
andj = 0
- Create array
-
Iterate through the array: For each element
v
at positioni
(1-indexed):a. Update the window's left boundary:
j = max(j, d[v])
- If
d[v] > 0
, it means we've seenv
before at positiond[v]
- We must start our window after the previous occurrence to maintain uniqueness
b. Calculate current window sum:
- Sum from position
j
toi-1
(0-indexed) =s[i] - s[j]
- Update
ans = max(ans, s[i] - s[j])
c. Record current position:
d[v] = i
to mark that valuev
was last seen at positioni
-
Return the maximum sum found
Example Walkthrough:
For nums = [4, 2, 4, 5, 6]
:
- Initially:
d = [0,0,0,0,0,0,0]
,s = [0,4,6,10,15,21]
,j = 0
- i=1, v=4:
j = max(0, 0) = 0
, sum =6-0 = 6
,d[4] = 1
- i=2, v=2:
j = max(0, 0) = 0
, sum =6-0 = 6
,d[2] = 2
- i=3, v=4:
j = max(0, 1) = 1
, sum =10-4 = 6
,d[4] = 3
- i=4, v=5:
j = max(1, 0) = 1
, sum =15-4 = 11
,d[5] = 4
- i=5, v=6:
j = max(1, 0) = 1
, sum =21-4 = 17
,d[6] = 5
- Maximum sum = 17
The time complexity is O(n)
as we make a single pass through the array, and space complexity is O(m)
where m
is the maximum value in the array.
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 the solution with nums = [1, 2, 1, 3]
:
Initial Setup:
- Create position tracker:
d = [0, 0, 0, 0]
(size = max(nums) + 1 = 4) - Build prefix sum:
s = [0, 1, 3, 4, 7]
s[0] = 0
s[1] = 0 + 1 = 1
s[2] = 1 + 2 = 3
s[3] = 3 + 1 = 4
s[4] = 4 + 3 = 7
- Initialize:
j = 0
(left boundary),ans = 0
(max sum)
Step-by-step Processing:
Position i=1, value v=1:
- Check last occurrence:
d[1] = 0
(never seen) - Update left boundary:
j = max(0, 0) = 0
- Valid window: indices [0] contains [1]
- Window sum:
s[1] - s[0] = 1 - 0 = 1
- Update max:
ans = max(0, 1) = 1
- Mark position:
d[1] = 1
Position i=2, value v=2:
- Check last occurrence:
d[2] = 0
(never seen) - Update left boundary:
j = max(0, 0) = 0
- Valid window: indices [0,1] contains [1,2]
- Window sum:
s[2] - s[0] = 3 - 0 = 3
- Update max:
ans = max(1, 3) = 3
- Mark position:
d[2] = 2
Position i=3, value v=1:
- Check last occurrence:
d[1] = 1
(seen at position 1!) - Update left boundary:
j = max(0, 1) = 1
- Valid window: indices [1,2] contains [2,1]
- Window sum:
s[3] - s[1] = 4 - 1 = 3
- Update max:
ans = max(3, 3) = 3
- Mark position:
d[1] = 3
Position i=4, value v=3:
- Check last occurrence:
d[3] = 0
(never seen) - Update left boundary:
j = max(1, 0) = 1
- Valid window: indices [1,2,3] contains [2,1,3]
- Window sum:
s[4] - s[1] = 7 - 1 = 6
- Update max:
ans = max(3, 6) = 6
- Mark position:
d[3] = 4
Result: Maximum sum = 6 (subarray [2,1,3])
The key insight: when we encounter a duplicate (the second 1 at position 3), we automatically adjust our window's left boundary to exclude the first occurrence, maintaining the uniqueness constraint while maximizing the sum.
Solution Implementation
1class Solution:
2 def maximumUniqueSubarray(self, nums: List[int]) -> int:
3 # Create a dictionary to store the last seen index of each number
4 # Using array instead of dict for O(1) access (assuming nums contains non-negative integers)
5 last_seen_index = [0] * (max(nums) + 1)
6
7 # Create prefix sum array for O(1) range sum calculation
8 # prefix_sums[i] = sum of nums[0:i], so prefix_sums[0] = 0
9 prefix_sums = list(accumulate(nums, initial=0))
10
11 # Initialize result and left boundary of current valid window
12 max_sum = 0
13 left_boundary = 0
14
15 # Iterate through the array with 1-indexed position
16 for current_pos, current_value in enumerate(nums, 1):
17 # Update left boundary to skip past the previous occurrence of current_value
18 # This ensures all elements in window [left_boundary, current_pos) are unique
19 left_boundary = max(left_boundary, last_seen_index[current_value])
20
21 # Calculate sum of current window and update maximum
22 # Window sum = prefix_sums[current_pos] - prefix_sums[left_boundary]
23 current_window_sum = prefix_sums[current_pos] - prefix_sums[left_boundary]
24 max_sum = max(max_sum, current_window_sum)
25
26 # Update the last seen position of current value
27 last_seen_index[current_value] = current_pos
28
29 return max_sum
30
1class Solution {
2 public int maximumUniqueSubarray(int[] nums) {
3 // Array to store the last seen index (1-based) of each number (0-10000)
4 int[] lastSeenIndex = new int[10001];
5
6 int n = nums.length;
7
8 // Prefix sum array to calculate subarray sums in O(1)
9 // prefixSum[i] = sum of nums[0] to nums[i-1]
10 int[] prefixSum = new int[n + 1];
11 for (int i = 0; i < n; i++) {
12 prefixSum[i + 1] = prefixSum[i] + nums[i];
13 }
14
15 int maxSum = 0;
16 int windowStart = 0; // Start index of current valid window
17
18 // Iterate through the array with end pointer
19 for (int windowEnd = 1; windowEnd <= n; windowEnd++) {
20 int currentNum = nums[windowEnd - 1];
21
22 // If current number was seen before, move window start
23 // to position after the last occurrence to maintain uniqueness
24 windowStart = Math.max(windowStart, lastSeenIndex[currentNum]);
25
26 // Calculate current window sum using prefix sum
27 // Sum from windowStart to windowEnd-1 = prefixSum[windowEnd] - prefixSum[windowStart]
28 int currentWindowSum = prefixSum[windowEnd] - prefixSum[windowStart];
29 maxSum = Math.max(maxSum, currentWindowSum);
30
31 // Update last seen index for current number (1-based indexing)
32 lastSeenIndex[currentNum] = windowEnd;
33 }
34
35 return maxSum;
36 }
37}
38
1class Solution {
2public:
3 int maximumUniqueSubarray(vector<int>& nums) {
4 // Array to store the last seen index of each element (1-indexed)
5 // Since element values are bounded by 10^4
6 int lastSeenIndex[10001]{};
7
8 int n = nums.size();
9
10 // Prefix sum array to calculate subarray sums in O(1)
11 // prefixSum[i] represents sum of elements from index 0 to i-1
12 int prefixSum[n + 1];
13 prefixSum[0] = 0;
14 for (int i = 0; i < n; ++i) {
15 prefixSum[i + 1] = prefixSum[i] + nums[i];
16 }
17
18 int maxSum = 0;
19 int windowStart = 0; // Start index of the current valid window (0-indexed)
20
21 // Iterate through the array using sliding window technique
22 for (int windowEnd = 1; windowEnd <= n; ++windowEnd) {
23 int currentValue = nums[windowEnd - 1];
24
25 // If current element was seen before, update window start
26 // to ensure all elements in the window are unique
27 windowStart = max(windowStart, lastSeenIndex[currentValue]);
28
29 // Calculate sum of current window and update maximum
30 int currentWindowSum = prefixSum[windowEnd] - prefixSum[windowStart];
31 maxSum = max(maxSum, currentWindowSum);
32
33 // Update the last seen index for current element (1-indexed)
34 lastSeenIndex[currentValue] = windowEnd;
35 }
36
37 return maxSum;
38 }
39};
40
1function maximumUniqueSubarray(nums: number[]): number {
2 // Find the maximum value in the array to determine the size of the position tracking array
3 const maxValue = Math.max(...nums);
4 const arrayLength = nums.length;
5
6 // Create prefix sum array to calculate subarray sums efficiently
7 // prefixSum[i] represents the sum of elements from index 0 to i-1
8 const prefixSum: number[] = Array.from({ length: arrayLength + 1 }, () => 0);
9 for (let i = 1; i <= arrayLength; ++i) {
10 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
11 }
12
13 // Track the last occurrence position (1-indexed) of each number
14 const lastPosition = Array.from({ length: maxValue + 1 }, () => 0);
15
16 let maxSum = 0;
17 let windowStart = 0; // Start index of the current valid window (0-indexed)
18
19 // Iterate through the array to find maximum sum of subarray with unique elements
20 for (let i = 1; i <= arrayLength; ++i) {
21 const currentNum = nums[i - 1];
22
23 // If current number was seen before, update window start to exclude the duplicate
24 windowStart = Math.max(windowStart, lastPosition[currentNum]);
25
26 // Calculate sum of current valid window and update maximum
27 const currentWindowSum = prefixSum[i] - prefixSum[windowStart];
28 maxSum = Math.max(maxSum, currentWindowSum);
29
30 // Update the last occurrence position of current number
31 lastPosition[currentNum] = i;
32 }
33
34 return maxSum;
35}
36
Time and Space Complexity
Time Complexity: O(n + m)
, where n
is the length of the array nums
and m
is the maximum value in nums
.
- Finding
max(nums)
takesO(n)
time - Creating the prefix sum array using
accumulate
takesO(n)
time - The main loop iterates through all
n
elements once, with each iteration performing constant time operations:O(n)
- Total:
O(n) + O(n) + O(n) = O(n)
whenm
is considered bounded or small relative ton
However, if we consider m
(the maximum value) as an independent variable, the complexity becomes O(n + m)
due to the array initialization d = [0] * (max(nums) + 1)
.
Space Complexity: O(n + m)
, where n
is the length of the array nums
and m
is the maximum value in nums
.
- The array
d
has sizemax(nums) + 1
, which requiresO(m)
space - The prefix sum array
s
has sizen + 1
, which requiresO(n)
space - Other variables (
ans
,j
,i
,v
) useO(1)
space - Total:
O(n + m)
When the maximum value m
is bounded by a constant or is proportional to n
, both complexities simplify to O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Memory Inefficiency with Large Values
The current solution creates an array of size max(nums) + 1
, which can be extremely memory-inefficient when the input contains large values. For instance, if nums = [1, 1000000]
, the solution allocates an array of size 1,000,001 just to track two values.
Solution: Use a dictionary/hashmap instead of an array for tracking last seen positions:
last_seen_index = {} # Instead of [0] * (max(nums) + 1)
# When accessing, use .get() with default value
left_boundary = max(left_boundary, last_seen_index.get(current_value, 0))
2. Off-by-One Errors with Index Management
The solution mixes 0-indexed arrays with 1-indexed position tracking, which can easily lead to confusion. The enumerate(nums, 1)
starts counting from 1, while array indices start from 0. This discrepancy makes the code harder to debug and maintain.
Solution: Stick to consistent 0-based indexing throughout:
for i in range(len(nums)):
current_value = nums[i]
# Update left boundary to be the index AFTER the last occurrence
if current_value in last_seen_index:
left_boundary = max(left_boundary, last_seen_index[current_value] + 1)
# Calculate sum from left_boundary to i (inclusive)
current_sum = prefix_sums[i + 1] - prefix_sums[left_boundary]
max_sum = max(max_sum, current_sum)
# Store current index (not position)
last_seen_index[current_value] = i
3. Integer Overflow in Other Languages
While Python handles arbitrarily large integers, implementing this solution in languages like Java or C++ could cause integer overflow when calculating sums for large arrays with large values.
Solution: Use appropriate data types (e.g., long long
in C++ or long
in Java) for sum calculations and consider the constraints carefully.
4. Incorrect Sliding Window Updates
A common mistake is forgetting to update the left boundary correctly when duplicates are found. Some might incorrectly set left_boundary = last_seen_index[current_value]
instead of taking the maximum with the current left boundary, which could move the window backward illegally.
Solution: Always ensure the window only moves forward:
# Correct: window never moves backward
left_boundary = max(left_boundary, last_seen_index[current_value])
# Incorrect: could move window backward
# left_boundary = last_seen_index[current_value]
Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?
Recommended Readings
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!