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

文章目录

一. 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);
	}
}
相关推荐
我是唐青枫12 小时前
Java Spring Security 实战详解:从登录认证到 JWT 权限控制
java·spring
ihuyigui12 小时前
海外签收通知短信接口
android·java·开发语言·前端·数据库·后端
海棠Flower未眠12 小时前
SpringBoot 消息死信队列(荣耀典藏版)
java·数据库·spring boot
暖和_白开水12 小时前
数据分析agent (七):contextvars 模块上下文request_id
java·前端·数据分析
QXWZ_IA13 小时前
桥梁数字孪生怎么落地?
人工智能·科技·算法·智能硬件·政务
Kel13 小时前
输出层与反分词(Output Layer & Detokenization)
人工智能·算法·架构
陕西企来客13 小时前
2026年7月技术好GEO优化方案:算法适配与内容策略
人工智能·算法·机器学习·技术好geo优化
风栖柳白杨13 小时前
【面试】AI算法工程师_空白自测版本
人工智能·算法·面试
weixin_7275356213 小时前
MinIO大文件上传深度拆解:从原理到生产落地的完整指南
java·中间件
摇滚侠14 小时前
Java 全栈开发实战教程 课程笔记 34-36
java·笔记