Facebook Pixel

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.

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

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 4s 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:

  1. d: An array (or hash table) where d[v] stores the last occurrence index of value v. We initialize it with size max(nums) + 1 for direct indexing.
  2. s: A prefix sum array where s[i] represents the sum of all elements from index 0 to i-1. We use accumulate(nums, initial=0) to build this.
  3. j: A pointer tracking the leftmost valid starting position of our current window.
  4. ans: Stores the maximum sum found so far.

Algorithm Steps:

  1. Initialize data structures:

    • Create array d with zeros, sized to accommodate the maximum value in nums
    • Build prefix sum array s where s[0] = 0 and s[i] = s[i-1] + nums[i-1]
    • Set ans = 0 and j = 0
  2. Iterate through the array: For each element v at position i (1-indexed):

    a. Update the window's left boundary:

    • j = max(j, d[v])
    • If d[v] > 0, it means we've seen v before at position d[v]
    • We must start our window after the previous occurrence to maintain uniqueness

    b. Calculate current window sum:

    • Sum from position j to i-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 value v was last seen at position i
  3. 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 Evaluator

Example 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) takes O(n) time
  • Creating the prefix sum array using accumulate takes O(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) when m is considered bounded or small relative to n

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 size max(nums) + 1, which requires O(m) space
  • The prefix sum array s has size n + 1, which requires O(n) space
  • Other variables (ans, j, i, v) use O(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]
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?


Recommended Readings

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

Load More