2640. Find the Score of All Prefixes of an Array
Problem Description
You are given a 0-indexed integer array nums
of length n
. The problem asks you to understand two concepts and then compute a result based on them.
First, let's understand the conversion array. For any array arr
, its conversion array conver
is defined such that each element at position i
equals:
conver[i] = arr[i] + max(arr[0..i])
Where max(arr[0..i])
represents the maximum value among all elements from index 0 to index i
(inclusive).
For example, if arr = [2, 1, 3]
:
conver[0] = 2 + max([2]) = 2 + 2 = 4
conver[1] = 1 + max([2, 1]) = 1 + 2 = 3
conver[2] = 3 + max([2, 1, 3]) = 3 + 3 = 6
Second, the score of an array is defined as the sum of all values in its conversion array. Using the example above, the score would be 4 + 3 + 6 = 13
.
Your task is to return an array ans
where each ans[i]
represents the score of the prefix nums[0..i]
. In other words:
ans[0]
is the score of the subarray containing justnums[0]
ans[1]
is the score of the subarraynums[0..1]
ans[2]
is the score of the subarraynums[0..2]
- And so on...
Each element in the answer array represents the cumulative score calculation up to that index, where the score at each position considers the conversion array values accumulated so far.
Intuition
Let's think about what happens when we compute the score for each prefix. For the prefix ending at index i
, we need to calculate the conversion array and then sum it up.
The key observation is that when we move from prefix nums[0..i-1]
to prefix nums[0..i]
, we're adding one more element to our consideration. The score of nums[0..i]
equals the score of nums[0..i-1]
plus the conversion value at index i
.
What's the conversion value at index i
? It's nums[i] + max(nums[0..i])
.
Now, here's the crucial insight: we don't need to recalculate everything from scratch for each prefix. As we iterate through the array, we can:
- Keep track of the maximum value seen so far (let's call it
mx
) - Calculate the conversion value for the current position as
nums[i] + mx
- Add this to the running score
This means ans[i] = ans[i-1] + nums[i] + mx
, where mx
is continuously updated to be the maximum of all elements from index 0 to i
.
The beauty of this approach is that we're building our answer incrementally. Each new score is just the previous score plus the new conversion value. We don't need to store the entire conversion array or recalculate sums - we just maintain:
- The running maximum (
mx
) - The cumulative score up to each position
This transforms what could be an O(n²) problem (recalculating max and sum for each prefix) into an O(n) solution with a single pass through the array.
Learn more about Prefix Sum patterns.
Solution Approach
The solution uses a prefix sum technique with a running maximum to efficiently compute the scores.
Here's the step-by-step implementation:
-
Initialize variables:
- Create an answer array
ans
of lengthn
to store the scores - Initialize
mx = 0
to track the maximum value seen so far
- Create an answer array
-
Single pass iteration: For each element
nums[i]
in the array:a. Update the maximum:
mx = max(mx, nums[i])
- This ensures
mx
always holds the maximum value fromnums[0]
tonums[i]
b. Calculate the current score:
- The conversion value at position
i
isnums[i] + mx
- If
i = 0
, this is our first element, soans[0] = nums[0] + mx
- If
i > 0
, we add the conversion value to the previous score:ans[i] = nums[i] + mx + ans[i-1]
-
Return the result:
- The array
ans
now contains the scores for all prefixes
- The array
The key formula being applied is:
ans[i] = nums[i] + mx + ans[i-1] (for i > 0) ans[0] = nums[0] + mx (for i = 0)
This approach cleverly uses the fact that each prefix score builds upon the previous one, eliminating the need to recalculate the entire conversion array and its sum for each prefix. The time complexity is O(n) with a single pass, and space complexity is O(n) for storing the answer 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, 4, 2, 3]
.
We'll track:
mx
: the running maximumans[i]
: the score for prefixnums[0..i]
Initial state:
mx = 0
ans = [?, ?, ?, ?]
Step 1: Process nums[0] = 1
- Update maximum:
mx = max(0, 1) = 1
- Calculate conversion value:
1 + 1 = 2
- Since this is the first element:
ans[0] = 2
ans = [2, ?, ?, ?]
Step 2: Process nums[1] = 4
- Update maximum:
mx = max(1, 4) = 4
- Calculate conversion value:
4 + 4 = 8
- Add to previous score:
ans[1] = ans[0] + 8 = 2 + 8 = 10
ans = [2, 10, ?, ?]
Step 3: Process nums[2] = 2
- Update maximum:
mx = max(4, 2) = 4
(no change) - Calculate conversion value:
2 + 4 = 6
- Add to previous score:
ans[2] = ans[1] + 6 = 10 + 6 = 16
ans = [2, 10, 16, ?]
Step 4: Process nums[3] = 3
- Update maximum:
mx = max(4, 3) = 4
(no change) - Calculate conversion value:
3 + 4 = 7
- Add to previous score:
ans[3] = ans[2] + 7 = 16 + 7 = 23
ans = [2, 10, 16, 23]
Verification:
Let's verify ans[2] = 16
represents the score of prefix [1, 4, 2]
:
- Conversion array:
[1+1=2, 4+4=8, 2+4=6]
- Score:
2 + 8 + 6 = 16
✓
The final answer is [2, 10, 16, 23]
.
Solution Implementation
1class Solution:
2 def findPrefixScore(self, nums: List[int]) -> List[int]:
3 """
4 Calculate the prefix score array where each element is the sum of:
5 - The current number
6 - The maximum value seen so far
7 - The previous prefix score (cumulative sum)
8
9 Args:
10 nums: List of integers
11
12 Returns:
13 List of prefix scores
14 """
15 n = len(nums)
16 result = [0] * n # Initialize result array with zeros
17 max_so_far = 0 # Track the maximum value seen up to current position
18
19 for i, current_num in enumerate(nums):
20 # Update the maximum value seen so far
21 max_so_far = max(max_so_far, current_num)
22
23 # Calculate prefix score for current position:
24 # current_num + max_so_far + previous_prefix_score
25 if i == 0:
26 # First element: no previous prefix score
27 result[i] = current_num + max_so_far
28 else:
29 # Add previous prefix score to current calculation
30 result[i] = current_num + max_so_far + result[i - 1]
31
32 return result
33
1class Solution {
2 /**
3 * Calculates the prefix score array where each element is the cumulative sum
4 * of conversion values. The conversion value at index i is nums[i] plus
5 * the maximum element seen so far from index 0 to i.
6 *
7 * @param nums Input integer array
8 * @return Array containing cumulative prefix scores
9 */
10 public long[] findPrefixScore(int[] nums) {
11 int arrayLength = nums.length;
12 long[] prefixScores = new long[arrayLength];
13 int maxValueSoFar = 0;
14
15 for (int i = 0; i < arrayLength; i++) {
16 // Update the maximum value encountered up to current index
17 maxValueSoFar = Math.max(maxValueSoFar, nums[i]);
18
19 // Calculate conversion value: current element + max value so far
20 long conversionValue = nums[i] + maxValueSoFar;
21
22 // Calculate prefix score: add conversion value to previous prefix score
23 if (i == 0) {
24 prefixScores[i] = conversionValue;
25 } else {
26 prefixScores[i] = conversionValue + prefixScores[i - 1];
27 }
28 }
29
30 return prefixScores;
31 }
32}
33
1class Solution {
2public:
3 vector<long long> findPrefixScore(vector<int>& nums) {
4 int n = nums.size();
5 vector<long long> prefixScores(n);
6 int maxSoFar = 0;
7
8 // Calculate prefix scores where each element's conversion value is:
9 // nums[i] + max(nums[0...i])
10 // The prefix score at index i is the sum of all conversion values from 0 to i
11 for (int i = 0; i < n; ++i) {
12 // Update the maximum element seen so far
13 maxSoFar = max(maxSoFar, nums[i]);
14
15 // Calculate conversion value for current element
16 long long conversionValue = nums[i] + maxSoFar;
17
18 // Calculate prefix score:
19 // - For first element (i == 0): just the conversion value
20 // - For other elements: previous prefix score + current conversion value
21 if (i == 0) {
22 prefixScores[i] = conversionValue;
23 } else {
24 prefixScores[i] = prefixScores[i - 1] + conversionValue;
25 }
26 }
27
28 return prefixScores;
29 }
30};
31
1/**
2 * Calculates the prefix score array where each element is the sum of:
3 * - The current element
4 * - The maximum element seen so far
5 * - The previous prefix score (cumulative sum)
6 *
7 * @param nums - Input array of numbers
8 * @returns Array where each element represents the cumulative prefix score
9 */
10function findPrefixScore(nums: number[]): number[] {
11 const arrayLength: number = nums.length;
12 const prefixScores: number[] = new Array(arrayLength);
13 let maxValueSoFar: number = 0;
14
15 for (let index = 0; index < arrayLength; index++) {
16 // Update the maximum value encountered up to current index
17 maxValueSoFar = Math.max(maxValueSoFar, nums[index]);
18
19 // Calculate prefix score:
20 // Current element + max value so far + previous cumulative score
21 const previousScore: number = index === 0 ? 0 : prefixScores[index - 1];
22 prefixScores[index] = nums[index] + maxValueSoFar + previousScore;
23 }
24
25 return prefixScores;
26}
27
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. The algorithm iterates through the array exactly once with a single for loop, performing constant-time operations (comparison, addition, and array access) at each iteration.
The space complexity is O(1)
if we ignore the space consumption of the answer array ans
. The only extra space used is for the variables n
, mx
, i
, and x
, which are all constant space. The answer array ans
of size n
is required for the output and is typically not counted in space complexity analysis for problems that require returning a result array.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Maximum Value Initialization
A common mistake is initializing max_so_far
with the first element of the array (nums[0]
) or with float('-inf')
. This can lead to incorrect calculations, especially when dealing with negative numbers.
Incorrect approach:
max_so_far = nums[0] # Wrong! This skips processing the first element properly
for i in range(len(nums)):
max_so_far = max(max_so_far, nums[i])
# ...
Why it's wrong: When processing nums[0]
, we need max_so_far
to be updated after comparing with nums[0]
. If we initialize with nums[0]
, we're essentially pre-computing the maximum for the first element.
Correct approach:
max_so_far = 0 # Start with 0 or handle the first element separately
for i in range(len(nums)):
max_so_far = max(max_so_far, nums[i])
# Now max_so_far correctly represents max(nums[0..i])
2. Misunderstanding the Conversion Array Calculation
Some might incorrectly calculate the conversion value by using the maximum before including the current element:
Incorrect approach:
for i in range(len(nums)):
if i == 0:
result[i] = nums[i] + nums[i] # Correct by coincidence
else:
result[i] = nums[i] + max_so_far + result[i-1]
max_so_far = max(max_so_far, nums[i]) # Updated AFTER use - wrong!
Why it's wrong: The conversion array at position i
should use max(nums[0..i])
which includes nums[i]
. Updating the maximum after using it means we're using max(nums[0..i-1])
instead.
Correct approach:
for i in range(len(nums)):
max_so_far = max(max_so_far, nums[i]) # Update FIRST
# Then use the updated max_so_far
if i == 0:
result[i] = nums[i] + max_so_far
else:
result[i] = nums[i] + max_so_far + result[i-1]
3. Confusion Between Score and Conversion Array
A critical misunderstanding is thinking that ans[i]
should be the conversion value at position i
rather than the cumulative score up to position i
.
Incorrect interpretation:
# Wrong: This just calculates the conversion array, not the prefix scores
for i in range(len(nums)):
max_so_far = max(max_so_far, nums[i])
result[i] = nums[i] + max_so_far # Missing cumulative sum!
Correct interpretation:
# Correct: Each ans[i] is the SUM of all conversion values from 0 to i
for i in range(len(nums)):
max_so_far = max(max_so_far, nums[i])
conversion_value = nums[i] + max_so_far
if i == 0:
result[i] = conversion_value
else:
result[i] = result[i-1] + conversion_value # Cumulative sum
4. Edge Case Handling with Empty or Single-Element Arrays
While the problem states the array has length n
, it's good practice to handle edge cases:
Robust solution:
def findPrefixScore(self, nums: List[int]) -> List[int]:
if not nums: # Handle empty array
return []
n = len(nums)
result = [0] * n
max_so_far = 0
for i in range(n):
max_so_far = max(max_so_far, nums[i])
if i == 0:
result[i] = nums[i] + max_so_far
else:
result[i] = nums[i] + max_so_far + result[i-1]
return result
Which of the following is a good use case for backtracking?
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
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!