Important Observation
Lets consider 2 consecutive histograms H[i]
and H[i+1]
. Lets assume H[i+1] < H[i]
In such a case, for all histograms X
with index > i + 1
when traversing left for L
, there is no point looking at H[i]
after looking at H[i+1]
. If H[i+1] > X
, obviously H[i] > X
as we already know H[i] > H[i+1]
.
Then, whats the next entry we would want to look at? We would want to look at the first histogram left of H[i+1]
with height less than H[i+1]
.
We traverse all histograms from left to right, maintain a stack of histograms. Every histogram is pushed to stack once. A histogram is popped from stack when a histogram of smaller height is seen. When a histogram is popped, we calculate the area with the popped histogram as smallest histogram. How do we get left and right indexes of the popped histogram – the current index tells us the ‘right index’ R
and index of previous item in stack is the ‘left index’ L
. Following is a rough
We process the elements in left-to-right order and maintain a stack of information about started but yet unfinished subhistograms.
Whenever a new element arrives it is subjected to the following rules.
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
rect_area = 0
stack = [] # We will just use a list to represent the stack
heights.append(0) # Prevents a few conditional statements to handle the final case
for i, v in enumerate(heights):
while stack and heights[stack[-1]] > v:
# if we are in this while loop, we know that there are currently items in the stack
# and that the height of our currently viewed bar is less than the top item in the stack
# and thus we need to pop it out and calculate the area of the bar before we can add
# the current index into our monotonic stack
height = heights[stack.pop()]
if stack:
# Notice that since we know the index of the current bar, we don't actually
# need to keep track of how many bars we popped for the width
width = i - stack[-1] - 1
else:
# No items in the stack? We popped all items out and thus the current bar <= to
# all bars in positions < i
width = i
rect_area = max(rect_area, height * width)
# We know that either the stack is empty at this point or all items in the stack are <=
# height[i]
stack.append(i)
return rect_area
Given an array find the next smaller element in array for each element without changing the original order of the elements.
For example, suppose the given array is 4,2,1,5,3
.
The resultant array would be 2,1,-1,3,-1
.
def find_next_smaller_elements(xs):
ys=[-1 for x in xs]
stack=[]
for i,x in enumerate(xs):
while len(stack)>0 and x<xs[stack[-1]]:
ys[stack.pop()]=x
stack.append(i)
return ys
>>> find_next_smaller_elements([4,2,1,5,3])
[2, 1, -1, 3, -1]
>>> find_next_smaller_elements([1,2,3,4,5])
[-1, -1, -1, -1, -1]
>>> find_next_smaller_elements([5,4,3,2,1])
[4, 3, 2, 1, -1]
>>> find_next_smaller_elements([1,3,5,4,2])
[-1, 2, 4, 2, -1]
>>> find_next_smaller_elements([6,4,2])
[4, 2, -1]
CSES: Nearest Smaller Values - Nearest smallest element to the left of the element in the array
source: https://cses.fi/problemset/task/1645/
int main() {
dsd(n);
vi V(n+1);
REP1(i, n) sd(V[i]);
vector<int> S; // stack
S.push_back(0);
vector<int> ans(n+1);
REP1(i, n){
while(V[S.back()] >= V[i]) S.pop_back();
ans[i] = S.back();
S.push_back(i);
}
REP1(i, n) printf("%d ", ans[i]);
printf("\n");
return 0;
}
// Linear time all nearest smaller values, standard stack-based algorithm.
// ansv_left stores indices of nearest smaller values to the left in res. -1 means no smaller value was found.
// ansv_right likewise looks to the right. v.size() means no smaller value was found.
void ansv_left(vector<int>& v, vector<int>& res) {
stack<pair<int, int> > stk; stk.push(make_pair(INT_MIN, v.size()));
for (int i = v.size()-1; i >= 0; i--) {
while (stk.top().first > v[i]) {
res[stk.top().second] = i; stk.pop();
}
stk.push(make_pair(v[i], i));
}
while (stk.top().second < v.size()) {
res[stk.top().second] = -1; stk.pop();
}
}
void ansv_right(vector<int>& v, vector<int>& res) {
stack<pair<int, int> > stk; stk.push(make_pair(INT_MIN, -1));
for (int i = 0; i < v.size(); i++) {
while (stk.top().first > v[i]) {
res[stk.top().second] = i; stk.pop();
}
stk.push(make_pair(v[i], i));
}
while (stk.top().second > -1) {
res[stk.top().second] = v.size(); stk.pop();
}
}
source: https://github.com/t3nsor/codebook/blob/master/ansv.cpp
There are n psychos standing in a line. Each psycho is assigned a unique integer from 1
to n
. At each step every psycho who has an id greater than the psycho to his right (if exists) kills his right neighbor in the line. Note that a psycho might kill and get killed at the same step.
You're given the initial arrangement of the psychos in the line. Calculate how many steps are needed to the moment of time such, that nobody kills his neighbor after that moment. Look notes to understand the statement more precise.
Input:
The first line of input contains integer n denoting the number of psychos, (1 ≤ n ≤ 10^5)
. In the second line there will be a list of n
space separated distinct integers each in range 1
to n
, inclusive — ids of the psychos in the line from left to right.
Example: In the first sample line of the psychos transforms as follows: [10 9 7 8 6 5 3 4 2 1]
→ [10 8 4]
→ [10]
. So, there are two steps.
const int MAX_N = 100010;
const int INF = 1000000;
int n;
int values[MAX_N], life[MAX_N];
stack<int> killers;
int main() {
scanf("%d ", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d ", &values[i]);
}
life[0] = INF;
killers.push(0);
for (int i = 1; i <= n; ++i) {
life[i] = 1;
while (killers.size() && values[i] > values[killers.top()]) {
life[i] = max(life[i], life[killers.top()] + 1);
killers.pop();
}
killers.push(i);
}
int sol = 0;
for (int i = 1; i <= n; ++i) {
if (life[i] < INF) {
sol = max(sol, life[i]);
}
}
printf("%d\n", sol);
return 0;
}
Ref: https://leetcode.com/contest/weekly-contest-314/problems/using-a-robot-to-print-the-lexicographically-smallest-string/ and https://www.geeksforgeeks.org/find-lexicographical-smallest-string-by-performing-the-given-operations-n-times/
string robotWithString(string s) {
int n = s.size();
string ans;
vector<char> V, last_min(n, s.back());
// V -> for stack simulation
// last_min[i] -> smallest char in the suffix[i:n]
for(int i=n-2;i>=0;i--){
last_min[i] = min(last_min[i+1], s[i]);
}
for(int i=0;i<n;i++){
char c = s[i];
while(!V.empty() && last_min[i] >= V.back()){
ans.push_back(V.back());
V.pop_back();
}
if(last_min[i] == s[i]){
ans.push_back(s[i]);
} else {
V.push_back(s[i]);
}
}
while(!V.empty()){
ans.push_back(V.back());
V.pop_back();
}
return ans;
}
The span of the stock's price today is defined as the maximum number of consecutive days (starting from today and going backward) for which the stock price was less than or equal to today's price.
Ref: https://leetcode.com/problems/online-stock-span/description/
int next(int price) {
int span = 1;
while(V.size() > 0 && V.back().first <= price){
span += V.back().second;
V.pop_back();
}
V.push_back({price, span});
return span;
}
https://leetcode.com/problems/sum-of-subarray-minimums/description/
int sumSubarrayMins(vector<int>& arr) {
vector<int> st;
int md = 1e9 + 7;
long long ans = 0;
for(int i=0;i<=arr.size();i++){
while(!st.empty() && (i == arr.size() || arr[i] <= arr[st.back()])){
int mid = st.back(); st.pop_back();
int prev = -1;
if(!st.empty()) prev = st.back();
long long count = 1ll*(i - mid)*(mid - prev);
ans += count * arr[mid];
ans %= md;
}
st.push_back(i);
}
return ans % md;
}
Explaination:
class Solution:
def sumSubarrayMins(self, arr: List[int]) -> int:
MOD = 10 ** 9 + 7
stack = []
sum_of_minimums = 0;
for i in range(len(arr) + 1):
# when i reaches the array length, it is an indication that
# all the elements have been processed, and the remaining
# elements in the stack should now be popped out.
while stack and (i == len(arr) or arr[stack[-1]] >= arr[i]):
# Notice the sign ">=", This ensures that no contribution
# is counted twice. right_boundary takes equal or smaller
# elements into account while left_boundary takes only the
# strictly smaller elements into account
mid = stack.pop()
left_boundary = -1 if not stack else stack[-1]
right_boundary = i
# count of subarrays where mid is the minimum element
count = (mid - left_boundary) * (right_boundary - mid)
sum_of_minimums += (count * arr[mid])
stack.append(i)
return sum_of_minimums % MOD