2110. Number of Smooth Descent Periods of a Stock
Problem Description
You are tasked with analyzing the price history of a stock, given as an array of integers named prices
. Each element in this array corresponds to the price of the stock on a specific day. A smooth descent period is identified when there is a sequence of one or more consecutive days where each day's stock price is exactly 1
lower than the previous day's price (except for the first day of this sequence, which does not have this restriction).
The goal is to calculate the total number of smooth descent periods in the stock price history provided by prices
.
Intuition
The intuition behind solving this problem is to traverse the prices
array and identify segments where the prices are decreasing by exactly 1
each day. These segments are the smooth descent periods we are interested in counting.
To achieve this, we can iterate over the prices
array using two pointers or indices. The first pointer i
marks the start of a potential descent period, while the second pointer j
explores the array to find the end of this descent. As long as the difference between the prices of two consecutive days (prices[j - 1] - prices[j]
) equals 1
, we continue moving j
forward, extending the current descent period.
Once we reach the end of a descent period (when the difference is not equal to 1
), we calculate the total number of descent periods within the segment marked by i
and j
. This calculation can be done by using the formula for the sum of the first n
natural numbers, as the number of descent periods formed by a contiguous descending sequence is equivalent to the sum of an arithmetic series starting from 1
. The formula ans += (1 + cnt) * cnt // 2
is used, where cnt
is the length of the current descent period.
After adding to the total count ans
, we set i
to the current position of j
and proceed to find the next potential descent period in the array. This process continues until we have traversed the entire array and evaluated all potential smooth descent periods.
By using this approach, we ensure a linear time complexity of O(n), where n
is the number of days in the prices
array, since each element is visited only once.
Learn more about Math and Dynamic Programming patterns.
Solution Approach
The solution uses a straightforward linear scan algorithm with two pointers to traverse the prices
array without the need for additional data structures. Here's a step-by-step breakdown of how it's implemented:
-
Initialize a variable
ans
to0
. This will hold the cumulative number of smooth descent periods. -
Set two pointers (or indices)
i
andj
.i
starts at0
, marking the beginning of a potential smooth descent sequence. -
Initiate a
while
loop that will run as long asi
is less than the length of theprices
array (n
). -
Inside the loop, increment
j
starting fromi+1
as long asj
is less thann
and the price difference between two consecutive days is exactly1
(prices[j - 1] - prices[j] == 1
). This locates the end of the current descending period. -
Once the end of a descent period is found, calculate the length of this descent period (
cnt = j - i
). This count represents the number of contiguous days in the current smooth descent period. -
Use the arithmetic series sum formula to find the total number of smooth descent periods in the current sequence. The formula to use is
(1 + cnt) * cnt // 2
, which is then added toans
. -
After computing the number of descent periods for the current segment, move
i
toj
, the position where the current descent segment ended. This is to start checking for a new descent period from this point forward. -
Repeat steps 4 to 7 until the entire
prices
array has been scanned. -
After completing the traversal, return the total count
ans
as the final answer.
The implementation uses the concept that the sum of an arithmetic series can calculate the number of distinct smooth descent periods within a given range. Since in each contiguous descent sequence, the difference is '1', it forms a series like 1, 2, ..., cnt
. The sum of these numbers, representing different lengths of smooth descent periods that can be formed, is calculated using the equation (1 + cnt) * cnt // 2
. By summing up such counts for all descending sequences found in prices
, we obtain the overall number of smooth descent periods in the entire array.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
To illustrate the solution approach, let's consider a small example where the given prices
array is [5, 4, 3, 2, 8, 7, 6, 5, 4, 10]
.
Let's walk through the steps outlined in the solution approach:
-
Initialize
ans
to0
. This is where we will accumulate the number of smooth descent periods. -
Set pointers
i
andj
to0
and1
, respectively, to start tracking a potential smooth descent period from the beginning of the array. -
We now enter the
while
loop sincei
(0) is less than the length of theprices
array (10). -
The first potential descent starts at index
0
with a price of5
. We start movingj
forward and find thatprices[0] - prices[1]
is1
, and the same goes forprices[1] - prices[2]
andprices[2] - prices[3]
, until we reachprices[3] - prices[4]
which is not1
(since2 - 8
is-6
). Therefore, we have identified the first descent period from index0
to3
. -
We calculate the length of this descent period:
cnt = j - i
which in this case is4 - 0 = 4
. -
Using the arithmetic series sum formula, we add the number of smooth descents in this sequence to
ans
:(1 + cnt) * cnt // 2
which is(1 + 4) * 4 // 2 = 10
. So now,ans = 10
. -
We move
i
to the position wherej
is now (i = j
), makingi
4
, and look for the next descent period starting from index4
. -
Now
j
moves forward again, and we see that we have a descent from8
at index4
to4
at index8
. We perform the same calculations as before and find thatcnt = j - i = 9 - 4 = 5
. The number of descents is(1 + cnt) * cnt // 2
which equals(1 + 5) * 5 // 2 = 15
, and we add this toans
making it now10 + 15 = 25
. -
Finally, we have finished the
while
loop sincej
has reached the end of theprices
array. -
Return the total count
ans
which is25
. This is the total number of smooth descent periods in the example price history.
This walkthrough demonstrates the process and calculations in the solution approach using the given problem description and solution strategy.
Solution Implementation
1class Solution:
2 def getDescentPeriods(self, prices: List[int]) -> int:
3 # Initialize the total number of descent periods
4 total_periods = 0
5
6 # Initialize the starting index and find the length of the prices list
7 start_index = 0
8 length = len(prices)
9
10 # Iterate through the prices list
11 while start_index < length:
12 # Find the consecutive descending sequence by checking the price difference
13 end_index = start_index + 1
14 while end_index < length and prices[end_index - 1] - prices[end_index] == 1:
15 end_index += 1
16
17 # Calculate the length of the descent sequence
18 descent_length = end_index - start_index
19
20 # Using the arithmetic series sum formula, (n/2)*(first term + last term),
21 # here it simplifies to (1 + descent_length) * descent_length / 2
22 # since the difference between consecutive terms is 1.
23 total_periods += (1 + descent_length) * descent_length // 2
24
25 # Move the start index to the end of the current descent sequence
26 start_index = end_index
27
28 # Return the total number of descent periods found
29 return total_periods
30
1class Solution {
2 public long getDescentPeriods(int[] prices) {
3 long totalDescentPeriods = 0; // Initialize the result variable to keep track of the total descent periods.
4 int n = prices.length; // Get the total number of elements in the prices array.
5
6 // Iterate over the prices array.
7 for (int start = 0, end = 0; start < n; start = end) {
8 // Initialize end to the next element after start for the next potential descent.
9 end = start + 1;
10
11 // Find a contiguous subarray where each pair of consecutive elements
12 // have difference equals to 1. This forms a descent period.
13 // The while loop will continue until the condition fails,
14 // indicating we've reached the end of the current descent period.
15 while (end < n && prices[end - 1] - prices[end] == 1) {
16 ++end;
17 }
18
19 // Calculate the length of the current descent period.
20 int periodLength = end - start;
21
22 // Using the arithmetic progression sum formula to count all individual
23 // and overlapping periods: n*(n+1)/2, where n is the length of the current descent.
24 totalDescentPeriods += (1L + periodLength) * periodLength / 2;
25 }
26
27 // Return the total number of descent periods found in the prices array.
28 return totalDescentPeriods;
29 }
30}
31
1class Solution {
2public:
3 long long getDescentPeriods(vector<int>& prices) {
4 long long totalDescentPeriods = 0; // Holds the sum of all descent periods
5 int sequenceLength = prices.size(); // Total number of elements in prices
6
7 // Loop through each price in the vector
8 for (int start = 0, end = 0; start < sequenceLength; start = end) {
9 end = start + 1;
10
11 // Continue to find descending contiguous subsequences where each
12 // element is one less than the previous one
13 while (end < sequenceLength && prices[end - 1] - prices[end] == 1) {
14 ++end;
15 }
16
17 // Calculate the length of the contiguous subsequence
18 int count = end - start;
19
20 // Add the total number of descent periods that can be
21 // formed with the subsequence of length 'count'
22 totalDescentPeriods += (1LL + count) * count / 2;
23 }
24
25 return totalDescentPeriods; // Return the final total of descent periods
26 }
27};
28
1// Counts the total number of "descent periods" in a given array of prices.
2// A "descent period" is defined as one or more consecutive days where the
3// price of a stock decreases by exactly 1 each day.
4function getDescentPeriods(prices: number[]): number {
5 let totalDescentPeriods = 0; // Store the total count of descent periods.
6 const pricesLength = prices.length; // The length of the 'prices' array.
7
8 // Iterate through the 'prices' array to identify descent periods.
9 for (let startIndex = 0, endIndex = 0; startIndex < pricesLength; startIndex = endIndex) {
10 endIndex = startIndex + 1;
11
12 // Check if the next price is one less than the current; if so, extend the descent period.
13 while (endIndex < pricesLength && prices[endIndex - 1] - prices[endIndex] === 1) {
14 endIndex++;
15 }
16
17 // Calculate the number of prices in the current descent period.
18 const descentPeriodLength = endIndex - startIndex;
19
20 // Calculate the count of descent periods using the formula for the sum of the first n integers.
21 totalDescentPeriods += Math.floor(((1 + descentPeriodLength) * descentPeriodLength) / 2);
22 }
23
24 // Return the total count of descent periods found in the 'prices' array.
25 return totalDescentPeriods;
26}
27
Time and Space Complexity
The provided Python code defines a method getDescentPeriods
which calculates the total number of "descent periods" within a list of prices. A descent period is defined as a sequence of consecutive days where the price of each day is one less than the price of the day before.
Time Complexity:
The time complexity of the code is O(n)
, where n
is the number of elements in the prices
list. This is because the function uses a while loop (and a nested while loop) that traverses the list exactly once. Each element is visited once by the outer loop, and the inner loop advances the index j
without revisiting any previously checked elements. The computations within the loops are simple arithmetic operations, which take constant time. Therefore, the number of operations increases linearly with the number of elements in the input list, resulting in a linear time complexity.
Space Complexity:
The space complexity of the code is O(1)
. The code only uses a constant amount of space (variables ans
, i
, n
, j
, and cnt
) that does not depend on the input list's size. It does not use any additional data structures that grow with the input size, so the amount of space used remains constant even as the size of the input list increases.
Learn more about how to find time and space complexity quickly using problem constraints.
Which of the following uses divide and conquer strategy?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
LeetCode 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 we
Want a Structured Path to Master System Design Too? Don’t Miss This!