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

文章目录

一. 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);
	}
}
相关推荐
凤山老林2 小时前
27-Java final 关键字
java·开发语言
卷毛的技术笔记2 小时前
从零到一:深入浅出分布式锁原理与Spring Boot实战(Redis + ZooKeeper)
java·spring boot·redis·分布式·后端·面试·java-zookeeper
武帝为此2 小时前
【特征选择方法】
算法·数学建模
天空属于哈夫克32 小时前
行为审计与全链路追踪:私域自动化执行的安全性设计
java·运维·微服务
skilllite作者2 小时前
SkillLite 技术演进笔记:Workspace、沙箱与进化
java·开发语言·前端·笔记·安全·agentskills
XS0301062 小时前
Agent 记忆管理
大数据·人工智能·算法
探物 AI2 小时前
【感知·算法】一文综述医学图像分割:从经典 U-Net 到 Mamba 的范式跃迁
算法
DevilSeagull2 小时前
Rust 结构体详解:从定义到实例化的指南
开发语言·算法·安全·rust
乐观勇敢坚强的老彭2 小时前
C++信奥洛谷循环章节练习题
java·c++·算法