🧠
Using Previous Less Element and Next Less Element (Monotonic Stack)
💡 This approach introduces the key insight that each element's contribution to the sum can be counted by knowing how far it extends as the minimum to the left and right, which can be found efficiently using monotonic stacks.
Intuition
For each element, find the distance to the previous smaller element and the next smaller element. The element contributes its value multiplied by the number of subarrays where it is the minimum.
Algorithm
- Initialize two arrays: prevLess and nextLess to store indices of previous and next smaller elements.
- Use a monotonic increasing stack to fill prevLess by scanning left to right.
- Use a monotonic increasing stack to fill nextLess by scanning right to left.
- For each element, calculate contribution = arr[i] * (i - prevLess[i]) * (nextLess[i] - i).
- Sum all contributions modulo 10^9+7 and return.
💡 The key is to understand how the product of distances counts the number of subarrays where the element is minimum.
def sumSubarrayMins(arr):
MOD = 10**9 + 7
n = len(arr)
stack = []
prevLess = [-1] * n
nextLess = [n] * n
# Find previous less element for each index
for i in range(n):
while stack and arr[stack[-1]] > arr[i]:
stack.pop()
prevLess[i] = stack[-1] if stack else -1
stack.append(i)
stack.clear()
# Find next less element for each index
for i in range(n - 1, -1, -1):
while stack and arr[stack[-1]] >= arr[i]:
stack.pop()
nextLess[i] = stack[-1] if stack else n
stack.append(i)
result = 0
for i in range(n):
left_count = i - prevLess[i]
right_count = nextLess[i] - i
result = (result + arr[i] * left_count * right_count) % MOD
return result
# Driver code
if __name__ == '__main__':
print(sumSubarrayMins([3,1,2,4])) # Expected output: 17
Line Notes
prevLess = [-1] * nInitialize with -1 meaning no smaller element to the left, setting boundary for counting.
while stack and arr[stack[-1]] > arr[i]:Pop indices with values greater than current to maintain monotonic increasing stack for previous less.
prevLess[i] = stack[-1] if stack else -1Assign previous less index or -1 if none found.
stack.clear()Reset stack to reuse for next less calculation scanning from right.
while stack and arr[stack[-1]] >= arr[i]:Pop indices with values greater or equal to current for next less to handle duplicates correctly.
result = (result + arr[i] * left_count * right_count) % MODAdd contribution of current element to total modulo to avoid overflow.
import java.util.*;
public class Solution {
public static int sumSubarrayMins(int[] arr) {
int MOD = 1000000007;
int n = arr.length;
int[] prevLess = new int[n];
int[] nextLess = new int[n];
Arrays.fill(prevLess, -1);
Arrays.fill(nextLess, n);
Deque<Integer> stack = new ArrayDeque<>();
// Previous less
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
stack.pop();
}
prevLess[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
stack.clear();
// Next less
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
stack.pop();
}
nextLess[i] = stack.isEmpty() ? n : stack.peek();
stack.push(i);
}
long result = 0;
for (int i = 0; i < n; i++) {
long leftCount = i - prevLess[i];
long rightCount = nextLess[i] - i;
result = (result + (long)arr[i] * leftCount * rightCount) % MOD;
}
return (int)result;
}
public static void main(String[] args) {
System.out.println(sumSubarrayMins(new int[]{3,1,2,4})); // Expected: 17
}
}
Line Notes
Arrays.fill(prevLess, -1);Initialize previous less indices to -1 meaning no smaller element to the left.
while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {Pop indices with values greater than current to maintain monotonic increasing stack.
prevLess[i] = stack.isEmpty() ? -1 : stack.peek();Assign previous less index or -1 if none.
stack.clear();Clear stack to reuse for next less calculation scanning from right.
while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {Pop indices with values greater or equal to current for next less to handle duplicates.
result = (result + (long)arr[i] * leftCount * rightCount) % MOD;Add contribution of element to result modulo.
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
int sumSubarrayMins(vector<int>& arr) {
const int MOD = 1000000007;
int n = arr.size();
vector<int> prevLess(n, -1), nextLess(n, n);
stack<int> st;
// Previous less
for (int i = 0; i < n; i++) {
while (!st.empty() && arr[st.top()] > arr[i]) {
st.pop();
}
prevLess[i] = st.empty() ? -1 : st.top();
st.push(i);
}
// Clear stack
while (!st.empty()) st.pop();
// Next less
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && arr[st.top()] >= arr[i]) {
st.pop();
}
nextLess[i] = st.empty() ? n : st.top();
st.push(i);
}
long long result = 0;
for (int i = 0; i < n; i++) {
long long leftCount = i - prevLess[i];
long long rightCount = nextLess[i] - i;
result = (result + (long long)arr[i] * leftCount * rightCount) % MOD;
}
return (int)result;
}
int main() {
vector<int> arr = {3,1,2,4};
cout << sumSubarrayMins(arr) << endl; // Expected: 17
return 0;
}
Line Notes
const int MOD = 1000000007;Initialize modulo constant as integer literal to avoid floating point issues.
while (!st.empty() && arr[st.top()] > arr[i]) {Pop indices with values greater than current to maintain monotonic increasing stack.
prevLess[i] = st.empty() ? -1 : st.top();Assign previous less index or -1 if none.
while (!st.empty() && arr[st.top()] >= arr[i]) {Pop indices with values greater or equal to current for next less to handle duplicates.
nextLess[i] = st.empty() ? n : st.top();Assign next less index or n if none.
result = (result + (long long)arr[i] * leftCount * rightCount) % MOD;Add contribution of element to result modulo.
function sumSubarrayMins(arr) {
const MOD = 1000000007;
const n = arr.length;
const prevLess = new Array(n).fill(-1);
const nextLess = new Array(n).fill(n);
const stack = [];
// Previous less
for (let i = 0; i < n; i++) {
while (stack.length && arr[stack[stack.length - 1]] > arr[i]) {
stack.pop();
}
prevLess[i] = stack.length ? stack[stack.length - 1] : -1;
stack.push(i);
}
stack.length = 0;
// Next less
for (let i = n - 1; i >= 0; i--) {
while (stack.length && arr[stack[stack.length - 1]] >= arr[i]) {
stack.pop();
}
nextLess[i] = stack.length ? stack[stack.length - 1] : n;
stack.push(i);
}
let result = 0;
for (let i = 0; i < n; i++) {
const leftCount = i - prevLess[i];
const rightCount = nextLess[i] - i;
result = (result + arr[i] * leftCount * rightCount) % MOD;
}
return result;
}
// Test
console.log(sumSubarrayMins([3,1,2,4])); // Expected: 17
Line Notes
const MOD = 1000000007;Initialize modulo constant as integer literal to avoid floating point issues.
while (stack.length && arr[stack[stack.length - 1]] > arr[i]) {Pop indices with values greater than current to maintain monotonic increasing stack.
prevLess[i] = stack.length ? stack[stack.length - 1] : -1;Assign previous less index or -1 if none.
stack.length = 0;Clear stack to reuse for next less calculation scanning from right.
while (stack.length && arr[stack[stack.length - 1]] >= arr[i]) {Pop indices with values greater or equal to current for next less to handle duplicates.
result = (result + arr[i] * leftCount * rightCount) % MOD;Add contribution of element to result modulo.
Each element is pushed and popped at most once in the monotonic stacks, so total time is linear. Extra arrays use linear space.
💡 For n=10^5, this means about 200,000 operations, which is efficient and feasible.
Interview Verdict: Accepted
This is the standard efficient solution using monotonic stacks and contribution counting.