LeetCode﹝前缀和ி﹞一维二维前缀和应用

Posted 白鳯

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode﹝前缀和ி﹞一维二维前缀和应用相关的知识,希望对你有一定的参考价值。

【LeetCode】﹝前缀和ி﹞一维、二维前缀和应用

在区间范围内统计奇数数目★

1523. 在区间范围内统计奇数数目

题目】给你两个非负整数 lowhigh 。请你返回 lowhigh 之间(包括二者)奇数的数目。

示例

输入:low = 3, high = 7
输出:3
解释:37 之间奇数数字为 [3,5,7]

解题思路

利用前缀和思想,high之前的奇数数目减去low之前的奇数数目

class Solution {
    public int countOdds(int low, int high) {
        return ((high + 1) >> 1) - (low >> 1);
    }
}

区域和检索 - 数组不可变★★

303. 区域和检索 - 数组不可变

题目】给定一个整数数组 nums,求出数组从索引 ij(i ≤ j)范围内元素的总和,包含i、j 两点。

实现 NumArray 类:

  • NumArray(int[] nums)使用数组nums初始化对象
  • int sumRange(int i, int j) 返回数组nums从索引ij(i ≤ j)范围内元素的总和,包含 i、j 两点(也就是 sum(nums[i], nums[i + 1], ... , nums[j])

提示:

  • 0 <= nums.length <= 104
  • -105 <= nums[i] <= 105
  • 0 <= i <= j < nums.length
  • 最多调用 104 次 sumRange方法

示例

输入:
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
输出:
[null, 1, -1, -3]
解释:
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1)) 
numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))

解题思路

class NumArray {
    int[] pre;

    public NumArray(int[] nums) {
        int n = nums.length;
        pre = new int[n + 1];
        for (int i = 0; i < n; i++) {
            pre[i + 1] = pre[i] + nums[i];
        }
    }
    
    public int sumRange(int left, int right) {
        return pre[right + 1] - pre[left];
    }
}

子数组异或查询★★

1310. 子数组异或查询

题目】有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]

对于每个查询 i,请你计算从 LiRiXOR值(即 arr[Li] xor arr[Li+1] xor ... xor arr[Ri])作为本次查询的结果。

并返回一个包含给定查询 queries 所有结果的数组。

提示

  • 1 <= arr.length <= 3 * 10^4
  • 1 <= arr[i] <= 10^9
  • 1 <= queries.length <= 3 * 10^4
  • queries[i].length == 2
  • 0 <= queries[i][0] <= queries[i][1] < arr.length

示例

输入:arr = [1,3,4,8], queries = [[0,1],[1,2],[0,3],[3,3]]
输出:[2,7,14,8] 
解释:
数组中元素的二进制表示形式是:
1 = 0001 
3 = 0011 
4 = 0100 
8 = 1000 
查询的 XOR 值为:
[0,1] = 1 xor 3 = 2 
[1,2] = 3 xor 4 = 7 
[0,3] = 1 xor 3 xor 4 xor 8 = 14 
[3,3] = 8

解题思路

可以利用前缀和的前提是异或运算的特点

  • x ^ 0 = x
  • x ^ x = 0
class Solution {
    public int[] xorQueries(int[] arr, int[][] queries) {
        int n = arr.length;
        int[] pre = new int[n + 1];
        for (int i = 0; i < n; i++) {
            pre[i + 1] = pre[i] ^ arr[i];
        }

        int m = queries.length;
        int[] res = new int[m];
        for (int i = 0; i < m; i++) {
            res[i] = pre[queries[i][0]] ^ pre[queries[i][1] + 1];
        }
        
        return res;
    }
}

定长子串中元音的最大数目★★

1456. 定长子串中元音的最大数目

题目】给你字符串 s和整数k

请返回字符串 s 中长度为 k的单个子字符串中可能包含的最大元音字母数。

英文中的 元音字母 为(a, e, i, o, u)。

提示:

  • 1 <= s.length <= 10^5
  • s 由小写英文字母组成
  • 1 <= k <= s.length

示例

输入:s = "leetcode", k = 3
输出:2
解释:"lee""eet""ode" 都包含 2 个元音字母。
--------------------------------------------
输入:s = "abciiidef", k = 3
输出:3
解释:子字符串 "iii" 包含 3 个元音字母。

解题思路

方法一:滑动窗口

class Solution {
    public int maxVowels(String s, int k) {
        int le = 0, ri = 0;
        int cur = 0, res = 0;
        while (ri < s.length()) {
            cur += check(s.charAt(ri));
            if (le + k - 1 == ri) {
                res = Math.max(res, cur);
                cur -= check(s.charAt(le));
                le++;
            }
            ri++;
        }
        return res;
    }

    private int check(char c) {
        char[] chars = {'a', 'e', 'i', 'o', 'u'};
        for (char ch : chars) {
            if (ch == c) {
                return 1;
            }
        }
        return 0;
    }
}

方法二:前缀和数组

class Solution {
    public int maxVowels(String s, int k) {
        int n = s.length();
        int[] pre = new int[n + 1];
        for (int i = 0; i < s.length(); i++) {
            pre[i + 1] = pre[i];
            pre[i + 1] += check(s.charAt(i));
        }

        int res = 0;
        for (int i = k; i <= n; i++) {
            res = Math.max(res, pre[i] - pre[i - k]);
        }
        return res;
    }

    private int check(char c) {
        char[] chars = {'a', 'e', 'i', 'o', 'u'};
        for (char ch : chars) {
            if (ch == c) {
                return 1;
            }
        }
        return 0;
    }
}

生存人数★★

面试题 16.10. 生存人数

题目】给定 N个人的出生年份和死亡年份,第i个人的出生年份为 birth[i],死亡年份为 death[i],实现一个方法以计算生存人数最多的年份。

你可以假设所有人都出生于 1900 年至 2000 年(含 1900 和 2000 )之间。如果一个人在某一年的任意时期处于生存状态,那么他应该被纳入那一年的统计中。例如,生于 1908 年、死于 1909 年的人应当被列入 1908 年和 1909 年的计数。

如果有多个年份生存人数相同且均为最大值,输出其中最小的年份。

提示:

  • 0 < birth.length == death.length <= 10000
  • birth[i] <= death[i]

示例

输入:
birth = {1900, 1901, 1950}
death = {1948, 1951, 2000}
输出: 1901

解题思路

使用前缀和数组,频次统计出生年份+1,死亡年份的后一年-1,之后求前缀和,取存活人数最多的第一个年份即可;

class Solution {
    public int maxAliveYear(int[] birth, int[] death) {
        int[] pre = new int[102];
        for (int i = 0; i < birth.length; i++) {
            pre[birth[i] - 1900]++;
            pre[death[i] - 1900  + 1]--;
        }

        int maxNum = pre[0];
        for (int i = 1; i < 102; i++) {
            pre[i] += pre[i - 1];
            maxNum = Math.max(maxNum, pre[i]);
        }

        int maxYear = 0;
        for (int i = 0; i < 102; i++) {
            if (pre[i] == maxNum) {
                maxYear = 1900 + i;
                break;
            }
        }
        
        return maxYear;
    }
}

二维区域和检索 - 矩阵不可变★★

304. 二维区域和检索 - 矩阵不可变

【题目】给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1),右下角为 (row2, col2)

在这里插入图片描述

上图子矩阵左上角(row1, col1) = (2, 1) ,右下角(row2, col2) = (4, 3),该子矩形内元素的总和为 8

提示:

  • 你可以假设矩阵不可变。
  • 会多次调用 sumRegion 方法*。*
  • 你可以假设 row1 ≤ row2col1 ≤ col2

【示例】

给定 matrix = [
  [3, 0, 1, 4, 2],
  [5, 6, 3, 2, 1],
  [1, 2, 0, 1, 5],
  [4, 1, 0, 1, 7],
  [1, 0, 3, 0, 5]
]

sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12

【解题思路】

二维前缀和图解如下,类似集合的并交补思想

在这里插入图片描述

查询区域和时只需在前缀和数组上采用同样的思路
区 间 和 = 右 下 区 间 和 − 左 端 区 间 和 − 上 端 区 间 和 + 左 上 区 间 和 区间和= 右下区间和-左端区间和-上端区间和+左上区间和 =+

class NumMatrix {
    int[][] pre;

    public NumMatrix(int[][] matrix) {
        int r = matrix.length, c = matrix[0].length;
        pre = new int[r + 1][c + 1];
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                pre[i + 1][j + 1] = pre[i + 1][j] + pre[i][j + 1] - pre[i][j] + matrix[i][j];
            }
        }
    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
        return pre[row2 + 1][col2 + 1] - pre[row2 + 1][col1] - 
               pre[row1][col2 + 1] + pre[row1][col1];
    }
}

矩阵区域和★★

1314. 矩阵区域和

题目】给你一个 m * n 的矩阵 mat和一个整数 K,请你返回一个矩阵 answer,其中每个answer[i][j]是所有满足下述条件的元素 mat[r][c]的和:

  • i - K <= r <= i + K, j - K <= c <= j + K
  • (r, c)在矩阵内。

提示

  • m == mat.length
  • n == mat[i].length
  • 1 <= m, n, K <= 100
  • 1 <= mat[i][j] <= 100

示例

输入:mat = [[1,2,3],[4,5,6],[7,8,9]], K = 2
输出:[[45,45,45],[45,45,45],[45,45,45]]

解题思路

同二维区域和检索一样,但要注意查询区间的边界

class Solution {
    public int[][] matrixBlockSum(int[][] mat, int k) {
        int r = mat.length, c = mat[0].length;
        int[][] pre = new int[r + 1][c + 1];
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                pre以上是关于LeetCode﹝前缀和ி﹞一维二维前缀和应用的主要内容,如果未能解决你的问题,请参考以下文章

前缀和—— 一维数组和二维数组的前缀和

AcWing 前缀和 一维加二维

前缀和差分模板

前缀和差分模板

浅谈二维前缀和

浅谈二维前缀和