2898. Maximum Linear Stock Score

MediumArrayHash Table
Leetcode Link

Problem Description

In this problem, we have an array prices that represents the price of a certain stock on different consecutive days, indexed starting from 1. The goal here is to select some of the stock prices in such a way that they form a linear selection. A selection is considered linear if the difference between the stock prices and their respective indices is constant for every pair of consecutive prices in the selection.

Formally, we say that a selection of indices is linear if the equation prices[indexes[j]] - prices[indexes[j - 1]] == indexes[j] - indexes[j - 1] holds for every consecutive pair of indices in the selection.

Your task is to maximize the score of such a selection, where the score is simply the sum of all stock prices in the selection.

The key to solving this problem lies in identifying that a linear selection of stock prices essentially forms an arithmetic sequence when considering the relationship between the prices and the indices. The challenge is to figure out which selections can produce the maximum sum while still adhering to the linearity condition.

Intuition

The solution to this problem hinges on recognizing that an arithmetic sequence has a constant difference between consecutive terms. Translating this to our problem, we're looking for those stock prices that maintain a constant difference between the price and its index for every pair in our selection.

In simpler terms, we want to find a subset of prices where (price - index) is the same for all of them. If we find the constant, that means we found an arithmetic subsequence. We can quickly test if an element belongs to an arithmetic subsequence by just subtracting its index from its price and checking if we have seen that result before.

To implement this intuition, we can use a hash table (in Python, a Counter object from the collections library) where the keys are the (price - index) value and the values are the sum of prices that correspond to those (price - index) values. The reason we sum the prices and not just count them is because we are interested in the sum of the prices, which corresponds to the "score" mentioned in the problem.

For each stock price, we calculate the value (prices[i] - i) and add the stock price to the sum in our hash table at that key. After we go through all stock prices, the maximum sum stored in the hash table gives us the maximum score of a linear selection. This way, we ensure we only examine the elements of prices once, giving us an efficient solution.

Solution Approach

The solution uses a hash table to keep track of the sum of all prices that share the same (price - index) value. The data structure used here is a Counter from the Python collections module, which is a subclass of the dictionary specifically designed to count hashable objects.

Let's break down the steps in the algorithm as follows:

  1. Create a Counter object, here denoted as cnt, which will act as our hash table.
  2. Iterate over the prices array using the index and value (i, x respectively). For each element:
    • Calculate the difference between the price x and the index i which gives you the necessary constant to check if a price belongs to a linear sequence.
    • Use this difference x - i as a key in our hash table. Accumulate the value x in cnt[x - i]. This means that for all prices which share the same (price - index) difference, their values will be added together in the hash table.
  3. After the iteration, we will have a hash table where each key represents a possible (price - index) difference, and the corresponding value is the sum of all prices that share that difference.
  4. The final step is to find the maximum value in the hash table cnt. The max(cnt.values()) operation will give us the highest sum of prices, which directly equates to the maximum possible score we can achieve with a linear selection.

The Reference Solution Approach succinctly states the transformation of the original equation in the problem to a simpler form that can be solved using a hash table. By keeping the sum of all prices that have the same (price - index) difference, we can easily retrieve the maximum score of a linear selection in O(n) time complexity, where n is the length of the prices array.

This approach significantly simplifies the problem as it avoids the need for nested loops or more complex data structures, effectively turning it into a single-pass solution with a final aggregate operation to find the maximum.

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's consider an example where the prices array is [3, 8, 1, 7, 10, 15].

Now, we will walk through the solution approach step by step:

  1. Create a Counter object: We start by creating a Counter called cnt that will hold the sums.

  2. Iterate over the prices array:

    • Index i = 1, Price x = 3:

      • Calculate the difference: x - i = 3 - 1 = 2.
      • Update the hash table: cnt[2] = 3.
    • Index i = 2, Price x = 8:

      • Difference: x - i = 8 - 2 = 6.
      • Update: cnt[6] = 8.
    • Index i = 3, Price x = 1:

      • Difference: x - i = 1 - 3 = -2.
      • Update: cnt[-2] = 1.
    • Index i = 4, Price x = 7:

      • Difference: x - i = 7 - 4 = 3.
      • Update: cnt[3] = 7.
    • Index i = 5, Price x = 10:

      • Difference: x - i = 10 - 5 = 5.
      • Update: cnt[5] = 10.
    • Index i = 6, Price x = 15:

      • Difference: x - i = 15 - 6 = 9.
      • Update: cnt[9] = 15.
  3. Hash Table (cnt) After the Iteration:

    • After iterating over all prices, our cnt will look like this:
    Counter({
      2: 3,
      6: 8,
      -2: 1,
      3: 7,
      5: 10,
      9: 15
    })

    Each key is a (price - index) difference, and each value is the sum of the prices that share that difference (in this case, just individual prices, as there are no repeat (price - index) differences).

  4. Find the Maximum Value in the Hash Table:

    • To find the maximum score, we look for the maximum value in cnt: max(cnt.values()), which is max([3, 8, 1, 7, 10, 15]).
    • The maximum score here is 15.

In this example, the maximum score corresponds to the price that has a unique (price - index) difference. In cases where there would be multiple prices sharing the same difference, their summed value would potentially be the maximum score.

The process demonstrates the power of the hashing technique to solve the problem efficiently. This way, we find the max score possible from a linear selection of stock prices in a single pass over the array.

Solution Implementation

1from collections import Counter
2
3class Solution:
4    def maxScore(self, prices: List[int]) -> int:
5        # Initialize a counter to keep track of the score
6        score_counter = Counter()
7
8        # Iterate through the list of prices with their respective indices
9        for index, price in enumerate(prices):
10            # Calculate the 'key' for this price by subtracting the index
11            key = price - index
12            # Add the price to the counter for the calculated key
13            score_counter[key] += price
14
15        # Return the maximum score found in the counter
16        # The values in the counter represent the cumulative score for each key
17        return max(score_counter.values())
18
1class Solution {
2    public long maxScore(int[] prices) {
3        // A HashMap to keep track of the sums of price contributions
4        Map<Integer, Long> contributionCounts = new HashMap<>();
5      
6        // Loop over the prices to calculate each contribution
7        for (int i = 0; i < prices.length; ++i) {
8            // We calculate each price's unique contribution key as the price minus the index
9            int contributionKey = prices[i] - i;
10            // We sum the actual price for each contribution key in the map
11            contributionCounts.merge(contributionKey, (long) prices[i], Long::sum);
12        }
13      
14        // Initialize the maximum score to zero
15        long maxScore = 0;
16        // Iterate over the values in the contributions map
17        for (long contributionSum : contributionCounts.values()) {
18            // Update maxScore to be the maximum of the current maxScore and the current contribution sum
19            maxScore = Math.max(maxScore, contributionSum);
20        }
21      
22        // Return the maximum score found
23        return maxScore;
24    }
25}
26
1#include <vector>
2#include <unordered_map>
3#include <algorithm> // for std::max
4
5class Solution {
6public:
7    // Method to compute the maximum score based on the given problem statement
8    long long maxScore(vector<int>& prices) {
9        // Using a hash map to store the computed value difference and sum of prices with the same difference
10        unordered_map<int, long long> countMap;
11
12        // Iterate through the prices array
13        for (int index = 0; index < prices.size(); ++index) {
14            // The key here is the value difference between 'prices[index]' and 'index'
15            // Simultaneously, aggregate the sum of prices that have the same value difference
16            countMap[prices[index] - index] += prices[index];
17        }
18
19        // Variable to store the maximum score
20        long long maxScore = 0;
21
22        // Iterate through the hash map to find the maximum aggregated sum of prices
23        for (auto& keyValue : countMap) {
24            // The first element of the pair (keyValue.first) is not used here
25            long long currentValue = keyValue.second;
26            // Update the 'maxScore' with the maximum value found so far
27            maxScore = std::max(maxScore, currentValue);
28        }
29
30        // Return the maximum score computed
31        return maxScore;
32    }
33};
34
1// This function calculates the maximum score based on a specific scoring rule.
2// The score for each number is determined by adding the number's value to the number of times
3// it appears at an index equal to its value minus its index.
4// For example, the number at index i contributes its value to the total score if it is equal to i.
5function maxScore(prices: number[]): number {
6    // Initialize the map to keep track of the score computed for each unique (price - index) pair
7    const scoreCount: Map<number, number> = new Map();
8
9    // Loop through each price in the array
10    for (let i = 0; i < prices.length; ++i) {
11        // Calculate the key for the map, which represents the unique price realization score
12        const scoreKey: number = prices[i] - i;
13
14        // Update the map with the new score, incrementing the existing score if the key already exists
15        // If the key doesn't exist, it initializes the score with the current price value
16        scoreCount.set(scoreKey, (scoreCount.get(scoreKey) || 0) + prices[i]);
17    }
18
19    // Find and return the largest score from the map
20    return Math.max(...scoreCount.values());
21}
22

Time and Space Complexity

The time complexity of the given code is O(n), where n is the length of the prices array. This is because the code iterates through each element of the prices array only once within a single for loop to build the counter, which in aggregate results in linear time complexity relative to the input size.

The space complexity of the code is also O(n) due to the use of a counter to store the frequency of each calculated value (x - i). In the worst case, where all (x - i) values are unique, the counter could contain n key-value pairs corresponding to the number of elements in the prices array. Hence, the space complexity is linear as well.

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


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

Which technique can we use to find the middle of a linked list?


Recommended Readings

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