文章目录
- [一. lintcode [1814 · 所有子数组之和](https://www.lintcode.com/problem/1814/)](#一. lintcode 1814 · 所有子数组之和)
-
- [1. 题目解析](#1. 题目解析)
- [2. 算法原理](#2. 算法原理)
- [3. 代码](#3. 代码)
- [二. 力扣 [907. 子数组的最小值之和](https://leetcode.cn/problems/sum-of-subarray-minimums/description/)](#二. 力扣 907. 子数组的最小值之和)
-
- [1. 题目解析](#1. 题目解析)
- [2. 算法原理](#2. 算法原理)
- [3. 代码](#3. 代码)
- [三. AcWing [蓝桥杯 2868. 子串分值](https://www.acwing.com/problem/content/2871/)](#三. AcWing 蓝桥杯 2868. 子串分值)
-
- [1. 题目解析](#1. 题目解析)
- [2. 算法原理](#2. 算法原理)
- [3. 代码](#3. 代码)
一. lintcode 1814 · 所有子数组之和
1. 题目解析
题意还是很好理解的, 找出所有子数组, 求出总和, 这道题在lintcode需要会员, 所以看博客写代码即可, 没必要去充值
2. 算法原理
又是一个新的算法思想, 贡献法, 巧妙地统计了一个元素在所有可能子数组出现的次数
3. 代码
java
public class Solution {
public int subArraySum(int[] nums) {
int n = nums.length;
int sum = 0;
for (int i = 0; i < n; i++) {
sum += nums[i] * (i + 1) * (n - i);
}
return sum;
}
}
二. 力扣 907. 子数组的最小值之和
1. 题目解析
和上道题类似, 也是所有子数组, 但这里是所有子数组的最小值之和
2. 算法原理
单调栈 + 贡献法
3. 代码
这里要注意计算最终和ret的时候可能会溢出, 要先转化为long再模上mod
java
class Solution {
public int sumSubarrayMins(int[] arr) {
int mod = (int)(1e9 + 7);
int n = arr.length;
int[] L = new int[n];
int[] R = new int[n];
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < n; i++) {
// 在栈不为空的前提下, 循环去栈中找到左面第一个<=arr[i]的下标, 不满足直接弹出
while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
stack.pop();
}
// 栈为空,l设置为-1,否则就为栈顶值
if (stack.isEmpty()) {
L[i] = -1;
}else {
L[i] = stack.peek();
}
// 上面i的左边界已经求完, 可以将当前下标i放入到栈中了
stack.push(i);
}
// 清空栈
stack.clear();
for (int i = n - 1; i >= 0; i--) {
// 在栈不为空的前提下, 循环去栈中找到右面第一个<arr[i]的下标, 不满足直接弹出
while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
stack.pop();
}
// 栈为空,r设置为n,否则就为栈顶值
if (stack.isEmpty()) {
R[i] = n;
}else {
R[i] = stack.peek();
}
// 上面i的右边界已经求完, 可以将当前下标i放入到栈中了
stack.push(i);
}
// 左右边界都求完, 开始计算贡献值*arr[i]
int ret = 0;
for(int i = 0; i < n; i++) {
ret = (int)((long)((long)(ret + (long)((long)(i - L[i]) * (long)(R[i] - i) * (long)arr[i]))) % mod);
}
return ret;
}
}
三. AcWing 蓝桥杯 2868. 子串分值
1. 题目解析
正难则反, 逆向思考, 我们不以一段字符串来考虑, 而是考虑每个字符单次出现的子区间(子数组)个数
2. 算法原理
3. 代码
注意最终ret可能会溢出, 这里定义为long
java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
String s = st.nextToken();
char[] c = s.toCharArray();
int n = c.length;
int[] hash = new int[27];
int[] L = new int[n];
int[] R = new int[n];
// 初始化
Arrays.fill(hash, -1);
Arrays.fill(L, -1);
Arrays.fill(R, n);
for (int i = 0; i < n; i++) {
int index = c[i] - 'a';
if (hash[index] != -1) {
L[i] = hash[index];
}
hash[index] = i;
}
// 再次初始化
Arrays.fill(hash, -1);
for (int i = n - 1; i >= 0; i--) {
int index = c[i] - 'a';
if (index >= 0 && index <= 25 && hash[index] != -1) {
R[i] = hash[index];
}
hash[index] = i;
}
long ret = 0;
for (int i = 0; i < n; i++) {
ret += (long)(i - L[i]) * (R[i] - i);
}
System.out.println(ret);
}
}





