算法奇妙屋(四十九)-贡献法

文章目录

一. 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);
	}
}
相关推荐
HugoStudio_SWAN5 分钟前
洛谷 P1319 / P1320 压缩技术——同一枚硬币的正反面
c++·学习·程序人生·算法
CCYe、19 分钟前
新模型上线、旧模型下线:企业AI网关如何管住模型版本
java·网络·数据库·人工智能
风筝在晴天搁浅21 分钟前
解题代码清爽版
java·开发语言
NeverFear79223 分钟前
【黑马点评】短信登录
java·redis·tomcat·intellij-idea
2601_962298671 小时前
正则表达式引擎 哪些
java·python·perl·pcre·正则表达式引擎
孙克旭_1 小时前
JVM 入门详解:JVM、JRE、JDK 区别,JVM 基础结构梳理
java·开发语言·jvm
随遇而安zx1 小时前
【地基篇】---JVM 内存结构知识点(Java 8 运行时数据区深度解析)
java·开发语言·jvm
布吉岛的石头1 小时前
Java 程序员第 48 阶段1:Transformer 架构总览与自注意力直觉,从 RNN 痛点理解注意力机制
java·架构·transformer
吴声子夜歌1 小时前
Guava——事件总线
java·网络·guava
en.en..1 小时前
Ubuntu嵌入式开发 export环境变量
java·开发语言·数据库