[Capriccio 10 Fan Wai] prefix and array

Posted by Malkavbug on Tue, 25 Jan 2022 14:54:21 +0100

Prefix and array

Learn from Small and beautiful algorithm skills: prefix and array

303. Region and retrieval - array immutable
304. Two dimensional area and retrieval - the matrix is immutable
560. Subarray with sum K
1314. Matrix areas and

The key to distinguish between prefix and sliding window is whether the element has a negative number

One dimensional prefix sum


303. Region and retrieval - array immutable

To match the prefix and array index with the original array index,
Prefix and array length is n+1
At this time, the prefix and array i position elements represent 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];
    }
}

2D prefix and

304. Two dimensional area and retrieval - the matrix is immutable


Two dimensional matrix prefix sum

Similarly, in order to facilitate management
When we build prefixes and arrays, the number of rows and columns should be 1 more than the original array
Prefix and array arr [1] [1] represents the sum of matrix elements from the first row to the first row and from the first column to the first column in the original array

such as

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] represents the sum of matrix elements from the first row to the second row and from the first column to the second column in the original array

The prefix established under this condition and the [i, j] element of the array represent the sum of the elements in the rectangle surrounded by [0, 0] to [i-1, j-1] in the original array.

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];
          // The first row and the first column have no practical significance, and the traversal starts with the row and column being 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 prefix and array arr [i, j], we need to use
arr[i-1,j]
arr[i,j-1]
arr[i-1,j-1]
Original array [i, j]
Elements in these four positions

Figure source leetcode - stupid pig blasting group - problem solution

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

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


Explanation of snow and candles in the upper picture source

//s(O,F) = arr[x1][y2+1] 
//s(O,E) = arr[x2+1][ y1]
//arr[x1][y1] is actually the part in the upper left corner. Because the area of this part is subtracted once more, it needs to be added back. 
arr[x2+1][y2+1]-arr[x1][y2+1]-arr[x2+1][y1]+arr[x1][y1]

Prefixes and ideas

560. Subarray with sum K

Violence Act
Generate prefix and array, and then subtract every two positions. If the value is the target number, res+1
Yes, but not elegant.

int subarraySum(int[] nums, int k) {
    int n = nums.length;
    // Construct prefix and
    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;
    // Enumerate all subarrays
    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;
}

Because we only care about the number of times and don't care about the specific solution, we can use hash table to speed up the operation

After reading the posts written by many people to describe the optimal solution, I finally found the version that I can really understand

Prefix and technique: solving the subarray problem

Because the time complexity is the largest, the expenditure is this part, which can be optimized

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

What's the second for loop doing? In the calculation, there are several j that can make the difference between sum[i] and sum[j] k. For each such j, add one to the result.

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

Swap the order of variables

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

If you can directly record how many times sum [J] and sum [i] - K are equal, it is equivalent to finding the result.

Because we don't ask which elements are specific, we only ask the number of times, so we can use map to record in the process of generating prefix and
Each prefix and the number of occurrences are put into the map and updated in real time

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

code:

class Solution {

    public int subarraySum(int[] nums, int k) {
        // Key: prefix sum, value: the number of prefix sum corresponding to key
        Map<Integer, Integer> preSumFreq = new HashMap<>();
        // For elements with subscript 0, the prefix sum is 0 and the number is 1
         //For example, if you enter [1,1,0], k = 2. If there is no such line of code, it will return 0. The cases of 1 + 1 = 2 and 1 + 1 + 0 = 2 are omitted
        //Input: [3,1,1,0] k = 2 will not be omitted
        //Because presum[3] - presum[0] represents the sum of the first three bits, a map is required Put (0,1), pad the bottom
        //Example source: Author: chefyuan
        //Link: 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;

            // First obtain the number of prefixes and prefix - K and add them to the count variable
            if (preSumFreq.containsKey(preSum - k)) {
             //   System.out.println("num= " + num +" preSum=  "+ preSum + " preSum-k= " + (preSum-k));
               // System.out.println("previous count =" "+ count");
                count += preSumFreq.get(preSum - k);
                //System.out.println("after count =" "+ count");
            }

            // Then maintain the definition of preSumFreq
            preSumFreq.put(preSum, preSumFreq.getOrDefault(preSum, 0) + 1);
        }
        return count;
    }
}

Questions similar to the idea of the algorithm:

Other prefixes and topics:

1314. Matrix areas and

Topics: Algorithm leetcode