[Caprice 10-Extra] Prefixes and arrays

prefix and array

Learn algorithm tricks that are beautiful since childhood: prefixes and arrays

303. Range Sum Retrieval - Immutable Arrays
304. 2D Range Sum Retrieval - Immutable Matrices
560. Sums as Subarrays of K
1314. Matrix Range Sums

The key to distinguish whether to use a prefix sum or a sliding window is: whether the element has a negative number

One-dimensional prefix sum

insert image description here
303. Ranges and Retrievals- Immutable Arrays

In order to correspond the prefix and the array subscript with the original array subscript, the
length of the prefix and the array is n+1.
At this time, the element at position i of the prefix and the array represents the sum of the first i elements in the original array.

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

two-dimensional prefix and

304. 2D Range Sum Retrieval - Immutable Matrix

insert image description here
2D matrix prefix sum

Similarly, for the convenience of management,
when we create the prefix and array, the number of rows and columns are 1 more than the original array. The
prefix and array arr [ 1 ] [ 1 ]represent 第一行到第一行 与第一列到第一列the sum of the matrix elements in the original array

for example

Original array:
3 1
5 6

Its prefix and array are
0 0 0
0 3 3+1
0 3+5 3+1+5+6
arr [ 2 ] [ 2 ]representing the sum of 第一行到第二行 与第一列到第二列the

The prefix and [i, j] elements of the array established under this condition 原数组represent the sum of the elements in the rectangle enclosed by [0, 0] to [i-1, j-1].

class NumMatrix {
    
    
    int [][] arr ;
    public NumMatrix(int[][] matrix) {
    
    
        int m = matrix.length, n = matrix[0].length;
      
        if (m == 0 || n == 0) return;
        arr = new int [m+1][n+1];
          // 第一行 第一列 无实际意义 从行列都是1 开始遍历
        for(int i=1;i<=m;i++){
    
    
            for(int j=1;j<=n;j++){
    
    
                arr[i][j]=arr[i-1][j]+arr[i][j-1]+matrix[i-1][j-1]-arr[i-1][j-1];
            }
        }
    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
    
    
        return arr[row2+1][col2+1]-arr[row1][col2+1]-arr[row2+1][col1]+arr[row1][col1];
    }
}

When solving the prefix sum of the prefix sum array arr[i,j], you need to use
arr[i-1,j]
arr[i,j-1]
arr[i-1,j-1]
the original array【 i, j] The source
of the element map of these four positions
insert image description here
leetcode-Benzhu Blasting Group-Problem Solution

Because arr[i, j] can be divided into four parts, that is, the four matrices with different colors in the above figure.
Use the sum of elements that have been calculated before to reduce the calculation amount of the current element. Otherwise, the elements in [i, j] will be traversed and accumulated every time. The time responsibility is very high.

Realize the sum of elements in the rectangle enclosed by [i, j] to [m, n] (m>= i , n >=j)

insert image description here
The picture above is from the solution of Negative Snow Bright Candle

//s(O,F) = arr[x1][y2+1] 
//s(O,E) = arr[x2+1][ y1]
//arr[x1][y1] 其实就是左上角的那一部分,因为多减去了一次这部分的面积,所以要加回来。 
arr[x2+1][y2+1]-arr[x1][y2+1]-arr[x2+1][y1]+arr[x1][y1]

prefixes and ideas

560. Subarrays Sum of K

insert image description here

Violent method
Generate a prefix and an array, and then subtract every two positions. If the value is the target number, res+1
can pass, but it is not elegant.

int subarraySum(int[] nums, int k) {
    
    
    int n = nums.length;
    // 构造前缀和
    int[] sum = new int[n + 1];
    sum[0] = 0; 
    for (int i = 0; i < n; i++)
        sum[i + 1] = sum[i] + nums[i];

    int ans = 0;
    // 穷举所有子数组
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < i; j++)
            // sum of nums[j..i-1]
            if (sum[i] - sum[j] == k)
                ans++;

    return ans;
}

Since we only care about the number of times and not the specific solution, we can use the hash table to speed up the operation

After reading many posts written by people describing this optimal solution, I finally found this version that I can actually understand

Prefixes and Tricks: Solving Subarray Problems

Because the largest expenditure of time complexity is this part, you can consider optimizing from here

for (int i = 1; i <= n; i++)
    for (int j = 0; j < i; j++)
        if (sum[i] - sum[j] == k)
            ans++;

What is the second for loop doing? The translation is, in the calculation, there are several j that can make the difference between sum[i] and sum[j] k. Each time such a j is found, the result is incremented by one.

sum[ i ] - sum [ j ] == k

swap the order of the variables

if (sum[j] == sum[i] - k)
    ans++;

If it is possible to directly record how many times sum [ j ] and sum [ i ]-k are equal, it is equivalent to finding the result.

Because you don’t ask for the specific elements, but only for the number of times, you can use map to record
each prefix and the number of occurrences in the process of generating the prefix sum, put it into the map, and update it in real time

Then before the next update, judge whether there is sum - k in the map, and if so, add the prefix and the number of occurrences.

code:

class Solution {
    
    

    public int subarraySum(int[] nums, int k) {
    
    
        // key:前缀和,value:key 对应的前缀和的个数
        Map<Integer, Integer> preSumFreq = new HashMap<>();
        // 对于下标为 0 的元素,前缀和为 0,个数为 1
         //例如输入[1,1,0],k = 2 如果没有这行代码,则会返回0,漏掉了1+1=2,和1+1+0=2的情况
        //输入:[3,1,1,0] k = 2时则不会漏掉
        //因为presum[3] - presum[0]表示前面 3 位的和,所以需要map.put(0,1),垫下底
        //例子来源:作者:chefyuan
        //链接:https://leetcode-cn.com/problems/subarray-sum-equals-k/solution/de-liao-yi-wen-jiang-qian-zhui-he-an-pai-yhyf/

        preSumFreq.put(0, 1);

        int preSum = 0;
        int count = 0;
        for (int num : nums) {
    
    
            preSum += num;

            // 先获得前缀和为 preSum - k 的个数,加到计数变量里
            if (preSumFreq.containsKey(preSum - k)) {
    
    
             //   System.out.println("num= " + num +" preSum=  "+ preSum + " preSum-k= " + (preSum-k));
               // System.out.println("之前 count ="+count );
                count += preSumFreq.get(preSum - k);
                //System.out.println("之后 count ="+count );
            }

            // 然后维护 preSumFreq 的定义
            preSumFreq.put(preSum, preSumFreq.getOrDefault(preSum, 0) + 1);
        }
        return count;
    }
}

Questions similar to this algorithm idea:

Other prefixes and titles:

1314. Matrix Regions and

Guess you like

Origin blog.csdn.net/qq_41852212/article/details/122638383