Facebook Pixel

2898. Maximum Linear Stock Score 🔒

MediumArrayHash Table
Leetcode Link

Problem Description

You are given a 1-indexed integer array prices, where prices[i] represents the price of a stock on the i-th day. Your task is to select a subset of elements from prices that forms a linear selection and maximizes the sum of selected prices.

A selection is defined by an array indexes (also 1-indexed) that contains k indices forming a subsequence of [1, 2, ..., n]. This selection is considered linear if it satisfies the following condition:

For every consecutive pair of indices in the selection (starting from the second index), the difference in prices must equal the difference in indices. Mathematically:

  • prices[indexes[j]] - prices[indexes[j-1]] = indexes[j] - indexes[j-1] for all 1 < j <= k

This means if you select days i and j (where j > i), the price increase per day must be exactly 1. For example, if you select day 2 with price 5 and day 5 with price 8, then 8 - 5 = 3 must equal 5 - 2 = 3.

The score of a selection is the sum of all selected prices: prices[indexes[1]] + prices[indexes[2]] + ... + prices[indexes[k]].

Your goal is to find the maximum score possible from any valid linear selection.

The key insight is that for any linear selection, all selected elements must satisfy prices[i] - i = constant. This transforms the problem into finding all elements with the same value of prices[i] - i and selecting the group with the maximum sum.

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

Intuition

Let's think about what the linear condition really means. When we have a linear selection with indices [i, j], the condition states: prices[j] - prices[i] = j - i

This can be rearranged to: prices[j] - j = prices[i] - i

This is the key observation! For any two elements to be part of the same linear selection, they must have the same value of prices[index] - index.

Let's extend this to three indices [i, j, k]. For this to be linear:

  • prices[j] - prices[i] = j - i implies prices[j] - j = prices[i] - i
  • prices[k] - prices[j] = k - j implies prices[k] - k = prices[j] - j

Therefore, prices[i] - i = prices[j] - j = prices[k] - k

This pattern holds for any number of elements in a linear selection. All elements in a valid linear selection must have the same value of prices[index] - index.

Now the problem becomes much simpler: instead of trying different combinations to form linear selections, we can:

  1. Group all indices by their prices[i] - i value
  2. For each group, all elements can form a valid linear selection together
  3. The score of each group is the sum of all prices[i] in that group
  4. The answer is the maximum score among all groups

This transforms a complex subsequence selection problem into a simple grouping and summation problem, which can be efficiently solved using a hash table to track the sum of prices for each distinct prices[i] - i value.

Solution Approach

Based on our intuition that all elements in a linear selection must satisfy prices[i] - i = constant, we can implement the solution using a hash table to group and sum elements efficiently.

Implementation Steps:

  1. Create a hash table (Counter): We use a Counter object cnt to store the sum of prices for each distinct value of prices[i] - i.

  2. Iterate through the array: For each index i and corresponding price x in the prices array:

    • Calculate the key: x - i (which represents prices[i] - i)
    • Add the price x to the sum for this key: cnt[x - i] += x

    Note that we use 0-based indexing in the implementation, but since we're computing differences, the relative differences remain the same as with 1-based indexing.

  3. Find the maximum sum: After processing all elements, each key in the hash table represents a different group where all elements can form a valid linear selection. The value for each key is the sum of all prices in that group. We return the maximum value among all groups: max(cnt.values()).

Example Walkthrough:

If prices = [3, 5, 7, 10] (using 0-based indexing):

  • Index 0: 3 - 0 = 3, add 3 to cnt[3]
  • Index 1: 5 - 1 = 4, add 5 to cnt[4]
  • Index 2: 7 - 2 = 5, add 7 to cnt[5]
  • Index 3: 10 - 3 = 7, add 10 to cnt[7]

Each element has a different prices[i] - i value, so each forms its own group. The maximum score would be 10 (the single element at index 3).

Time and Space Complexity:

  • Time: O(n) where n is the length of the prices array
  • Space: O(n) for the hash table in the worst case when all elements have different prices[i] - i values

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 a concrete example with prices = [1, 5, 3, 7, 8] (using 1-based indexing as per the problem).

Step 1: Calculate prices[i] - i for each element

Index (i)Priceprices[i] - i
111 - 1 = 0
255 - 2 = 3
333 - 3 = 0
477 - 4 = 3
588 - 5 = 3

Step 2: Group elements by their prices[i] - i value

  • Group with value 0: indices [1, 3] with prices [1, 3]
  • Group with value 3: indices [2, 4, 5] with prices [5, 7, 8]

Step 3: Verify linear selections are valid

Let's verify group 3 forms a valid linear selection:

  • Indices: [2, 4, 5]
  • Between indices 2 and 4: prices[4] - prices[2] = 7 - 5 = 2 and 4 - 2 = 2
  • Between indices 4 and 5: prices[5] - prices[4] = 8 - 7 = 1 and 5 - 4 = 1

Step 4: Calculate scores for each group

  • Group 0: sum = 1 + 3 = 4
  • Group 3: sum = 5 + 7 + 8 = 20

Step 5: Return the maximum score

The maximum score is 20, achieved by selecting indices [2, 4, 5] with prices [5, 7, 8].

This selection forms a linear pattern where the price increases by exactly 1 for each day gap in the selection.

Solution Implementation

1from typing import List
2from collections import Counter
3
4class Solution:
5    def maxScore(self, prices: List[int]) -> int:
6        # Create a counter to group elements by (value - index)
7        # Elements with the same (value - index) can be selected together
8        group_sums = Counter()
9      
10        # Iterate through prices with their indices
11        for index, price in enumerate(prices):
12            # Group key is (price - index)
13            # Add the price to the sum for this group
14            group_key = price - index
15            group_sums[group_key] += price
16      
17        # Return the maximum sum among all groups
18        return max(group_sums.values())
19
1class Solution {
2    public long maxScore(int[] prices) {
3        // Map to store the sum of prices grouped by (price - index)
4        // Key: (price - index), Value: sum of all prices with the same (price - index)
5        Map<Integer, Long> groupSums = new HashMap<>();
6      
7        // Iterate through all prices and group them by (price - index)
8        for (int index = 0; index < prices.length; index++) {
9            // Calculate the group key: price - index
10            int groupKey = prices[index] - index;
11          
12            // Add the current price to the sum of its group
13            // If the key doesn't exist, initialize with the current price
14            // If it exists, add the current price to the existing sum
15            groupSums.merge(groupKey, (long) prices[index], Long::sum);
16        }
17      
18        // Find the maximum sum among all groups
19        long maxSum = 0;
20        for (long currentSum : groupSums.values()) {
21            maxSum = Math.max(maxSum, currentSum);
22        }
23      
24        return maxSum;
25    }
26}
27
1class Solution {
2public:
3    long long maxScore(vector<int>& prices) {
4        // Map to store the sum of prices for each group
5        // Key: (price - index), Value: sum of prices in this group
6        unordered_map<int, long long> groupSums;
7      
8        // Group items by (price - index) and calculate sum for each group
9        // Items with the same (price - index) can be picked together
10        // because picking item j after item i requires: prices[j] - prices[i] >= j - i
11        // which can be rewritten as: prices[j] - j >= prices[i] - i
12        for (int i = 0; i < prices.size(); ++i) {
13            int groupKey = prices[i] - i;
14            groupSums[groupKey] += prices[i];
15        }
16      
17        // Find the maximum sum among all groups
18        long long maxSum = 0;
19        for (const auto& [key, sum] : groupSums) {
20            maxSum = max(maxSum, sum);
21        }
22      
23        return maxSum;
24    }
25};
26
1/**
2 * Calculates the maximum score from an array of prices
3 * The score is calculated by grouping elements where price[i] - i is the same
4 * and summing their values
5 * @param prices - Array of price values
6 * @returns Maximum sum among all groups
7 */
8function maxScore(prices: number[]): number {
9    // Map to store the sum of prices for each group
10    // Key: price[i] - i (the grouping criterion)
11    // Value: sum of all prices in this group
12    const groupSums: Map<number, number> = new Map();
13  
14    // Iterate through all prices
15    for (let index = 0; index < prices.length; ++index) {
16        // Calculate the group key for current element
17        const groupKey = prices[index] - index;
18      
19        // Add current price to its corresponding group sum
20        // If group doesn't exist, initialize with 0
21        const currentSum = groupSums.get(groupKey) || 0;
22        groupSums.set(groupKey, currentSum + prices[index]);
23    }
24  
25    // Return the maximum sum among all groups
26    return Math.max(...groupSums.values());
27}
28

Time and Space Complexity

The time complexity is O(n), where n is the length of the prices array. This is because:

  • The code iterates through the prices array once using enumerate(), which takes O(n) time
  • For each element, it performs constant time operations: computing x - i and updating the counter with cnt[x - i] += x, which are both O(1) operations
  • Finding the maximum value in the counter using max(cnt.values()) takes O(k) time where k is the number of unique keys, and in the worst case k ≤ n

The space complexity is O(n), where n is the length of the prices array. This is because:

  • The Counter object cnt stores key-value pairs where keys are x - i values
  • In the worst case, all elements in prices could produce unique x - i values, resulting in n different keys in the counter
  • Therefore, the space required is proportional to the input size n

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Confusion Between 0-based and 1-based Indexing

One of the most common pitfalls is mixing up the indexing systems. The problem statement explicitly mentions that the array is 1-indexed, but most programming languages (including Python) use 0-based indexing.

The Pitfall: Developers might incorrectly adjust the formula by adding 1 to the index:

# INCORRECT approach
for index, price in enumerate(prices):
    group_key = price - (index + 1)  # Wrong! Trying to convert to 1-based
    group_sums[group_key] += price

Why This is Wrong: The key insight is that we're looking for elements where the difference between price and index is constant. Since we're dealing with differences, the relative relationships remain the same whether we use 0-based or 1-based indexing:

  • In 1-based: prices[i] - i = constant
  • In 0-based: prices[i] - i = constant (the constant value differs by 1, but grouping remains the same)

The Solution: Use the indexing system natural to your programming language without adjustment:

# CORRECT approach
for index, price in enumerate(prices):
    group_key = price - index  # Direct subtraction works correctly
    group_sums[group_key] += price

2. Empty Array Edge Case

Another pitfall is not handling the edge case where the input array is empty.

The Pitfall:

def maxScore(self, prices: List[int]) -> int:
    group_sums = Counter()
    for index, price in enumerate(prices):
        group_sums[price - index] += price
    return max(group_sums.values())  # Fails if prices is empty!

The Solution: Add a check for empty input:

def maxScore(self, prices: List[int]) -> int:
    if not prices:
        return 0
  
    group_sums = Counter()
    for index, price in enumerate(prices):
        group_sums[price - index] += price
    return max(group_sums.values())

3. Misunderstanding the Linear Selection Condition

Some developers might incorrectly interpret the condition and try to find arithmetic progressions in the prices array directly, rather than understanding that the constraint relates price differences to index differences.

The Pitfall: Attempting to find consecutive elements where prices[i+1] - prices[i] = 1, which is NOT what the problem asks for.

The Solution: Remember that the condition is about the relationship between price differences and index differences for selected elements (which may not be consecutive in the original array). The correct interpretation leads to the insight that all selected elements must have the same value of prices[i] - i.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following array represent a max heap?


Recommended Readings

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

Load More