单调栈 + 贡献法
蓝桥杯子串分值
https://www.lanqiao.cn/problems/499/learning/
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
static int result = 0;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String S = scan.nextLine();
char[] array = S.toCharArray();
int len = array.length;
for(int i = 0; i < len; i++){
int left = 0;
int right = 0;
for(int j = i - 1; j >= 0 && array[j] != array[i]; j--){
left++;
}
for(int j = i + 1; j < len && array[j] != array[i]; j++){
right++;
}
result += (left + 1) * (right + 1);
}
System.out.println(result);
scan.close();
}
}
lc.907. 子数组的最小值之和
https://leetcode.cn/problems/sum-of-subarray-minimums/description/
方法1:不使用单调栈做法 left right记录长度
class Solution {
private static final long MOD = (long) 1e9 + 7;
public int sumSubarrayMins(int[] arr) {
long res = 0L;
int n = arr.length;
//计算贡献度
for(int i = 0; i < n; i++){
int left = 0;
int right = 0;
//避免相同元素重复计算,因此left或者right有一边需要统计大于等于就暂停
//本做法的left right是长度 也可以使用下标法
//根据乘法原理arr[i]贡献度为arr[i] * (left + 1) * (right + 1)
for(int j = i - 1; j >= 0 && arr[j] >= arr[i]; j--) left++;
for(int j = i + 1; j < n && arr[j] > arr[i]; j++) right++;
int f = (left + 1) * (right + 1);
res += (long)f * arr[i];
}
return (int) (res % MOD);
}
}
方法2:使用单调栈记录(L,R)的下标
class Solution {
private static final long MOD = (long) 1e9 + 7;
public int sumSubarrayMins(int[] arr) {
int n = arr.length;
int[] left = new int[n];
Stack<Integer> st = new Stack<>();
st.push(-1);
//维护一个严格单调递增的单调栈
//找到左边小于arr[i]的最近元素位置(不存在-1)
for(int i = 0; i < n; i++){
while(st.size() > 1 && arr[st.peek()] >= arr[i])
st.pop();
left[i] = st.peek();
st.push(i);
}
//找到右边小于等于arr[i]的最近元素位置(不存在n)
int[] right = new int[n];
st.clear();
st.push(n);
for(int i = n - 1; i >= 0; i--){
while(st.size() > 1 && arr[st.peek()] > arr[i])
st.pop();
right[i] = st.peek();
st.push(i);
}
long res = 0L;
//计算贡献度
for(int i = 0; i < n; i++){
res += (long) arr[i] * (i - left[i]) * (right[i] - i);
}
return (int) (res % MOD);
}
}
lc.1856 前缀和数组 + 单调栈
https://leetcode.cn/problems/maximum-subarray-min-product/description/
前缀和数组图解:
来源labuladong的算法小抄
本题要求子数组范围内的和,如果暴力遍历加法会超时,使用前缀和可以解决
class Solution {
private static final long MOD = (long) 1e9 + 7;
public int maxSumMinProduct(int[] nums) {
if(nums == null || nums.length == 0) return 0;
int n = nums.length;
//前缀和数组
long[] pre = new long[n + 1];
for(int i = 1; i <= n; i++){
pre[i] = pre[i-1] + nums[i-1];
}
//记录左边第一个小于nums[i]的下标
int[] left = new int[n];
Stack<Integer> st = new Stack<>();
st.push(-1);
for(int i = 0; i < n; i++){
while(st.size() > 1 && nums[st.peek()] >= nums[i])
st.pop();
left[i] = st.peek();
st.push(i);
}
//记录右边第一个小于等于nums[i]的下标
int[] right = new int[n];
st.clear();
st.push(n);
for(int i = n - 1; i >= 0; i--){
while(st.size() > 1 && nums[st.peek()] > nums[i])
st.pop();
right[i] = st.peek();
st.push(i);
}
long res = 0L;
for(int i = 0; i < n; i++){
long sum = pre[right[i]] - pre[left[i] + 1];
res = Math.max(res,(long) nums[i] * sum);
}
return (int) (res % MOD);
}
}