简化题意:给定正整数数组A,求A中所有连续子序列的最小值之和。
思路:
1.找到所有的连续子序列,将最小值相加
2.每个数成为连续子序列最小值的个数*这个数 -> 寻找每个数左侧第一个小于它的数和右侧第一个小于它的数
->连续子序列个数=[左侧>=个数]*[右侧>=个数]
变形:最大值相加
解法一 BST 352ms
vector<pair<int, int>> loc - number, position [sort by number]
set<int> locSet - the postions of numbers that are smaller than the current number
class Solution {
public:
int sumSubarrayMins(vector<int>& A) {
vector<pair<int,int>> loc;
int n = A.size();
for(int i=0;i<n;i++){
loc.emplace_back(A[i], i);
}
sort(loc.begin(), loc.end());
long long int sum = 0;
set<int> locSet;
for(int i=0;i<n;i++){
int index = loc[i].second;
long long int val = loc[i].first;
int left=0;
int right=0;
auto it = locSet.lower_bound(index);
if(it!=locSet.begin()){
it--;
left = index-*it-1;
}else{
left=index;
}
it = locSet.upper_bound(index);
if(it!=locSet.end()){
right = *it-index-1;
}else{
right=n-index-1;
}
sum += val * (left + 1) * (right + 1) % 1000000007;
sum %= 1000000007;
locSet.insert(index);
}
return sum;
}
};
解法二 two stacks
class Solution {
public:
int sumSubarrayMins(vector<int>& A) {
stack<pair<int,int>> leftStack, rightStack;
int n = A.size();
int left[n], right[n];
for(int i=0;i<n;i++){
while(!leftStack.empty() && leftStack.top().first>=A[i]){
leftStack.pop();
}
if(leftStack.empty()){
left[i] = i;
} else{
left[i] = i - leftStack.top().second -1;
}
leftStack.emplace(A[i], i);
}
for(int i=n-1;i>=0;i--){
while(!rightStack.empty() && rightStack.top().first>A[i]){
rightStack.pop();
}
if(rightStack.empty()){
right[i] = n-i-1;
} else{
right[i] = rightStack.top().second-i-1;
}
rightStack.emplace(A[i], i);
}
long long int sum = 0;
for(int i=0;i<n;i++){
sum += (left[i]+1)*(right[i]+1)*(long long int) A[i] % 1000000007;
sum %= 1000000007;
}
return sum;
}
};
解法三 one stack
Reference:
https://zhanghuimeng.github.io/post/leetcode-907-sum-of-subarray-minimums/