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

文章目录

一. 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 分钟前
瑞芯微(EASY EAI)RV1126B 模型部署API说明
linux·开发语言·网络·人工智能·深度学习·算法·yolo
東隅已逝,桑榆非晚13 分钟前
深⼊理解指针(5)
c语言·笔记·算法
Gerardisite13 分钟前
企业微信客户管理系统实战:标签、分层与自动化流程搭建
java·python·机器人·自动化·企业微信
ch.ju17 分钟前
Java程序设计(第3版)第三章——数组的定义方式
java·开发语言
lwf00616417 分钟前
顺序模型学习日记
算法
_日拱一卒21 分钟前
LeetCode:199二叉树的右视图
算法·leetcode·职场和发展
The Chosen One98528 分钟前
分享对dp题目的理解-不断更新ing
笔记·算法·深度优先·动态规划·dp
有时间要学习32 分钟前
【无标题】
算法
Chloeis Syntax32 分钟前
JavaEE学习日记(2)---文件操作和IO
java·笔记·学习·java-ee
无风听海33 分钟前
OAuth 2.0 response_type完全指南
java·开发语言·oauth