Google Interview Question
Country: United States
You can precompute the number of ones at every position in a way that:
NumOfOnes(i, j) = NumberOfOnes(0-i, 0-j).... in other words at any index you should be able to tell the number of ones until that index from 0,0.
This precomputation will take O(n^2) time & O(n^2) memory but we will have to do so only once. Once you have that then finding number of ones is in subset is simply O(1) irrespective the size of grid.
public class Solution {
private final Integr[][] cachedSum;
private final Integer[][] matrix;
public Solution(Integer[][] matrix) {
if(matrix == null || matrix.length == 0 || matrix[0].length == 0) {
throw new IllegalArgumentException("Pass in valid matrix");
}
this.matrix = matrix;
Map<Point, Integer> cache = new HashMap<>();
this.cachedSum = this.getPreCalculatedSum(matrix, matrix.length - 1, matrix[0].length - 1, cache);
}
// This is the api that will be called everytime and returns in O(1)
public int getSum(int row1, int col1, int row2, int col2) {
if(row1 < 0 || col1 < 0 || row1 > row2 || col1 > col2 || row2 >= matrix.length || col2 >= matrix[0].length) {
throw new IllegalArgumentException("Please pass in valid cordiates");
}
int topDiagonalSum = row1 - 1 >= 0 && col1 - 1 >= 0 ? this.cachedSum[row1 - 1][col1 - 1] : 0;
int topSectionSum = row1 - 1 >= 0 ? this.cachedSum[row1 - 1][col2] : 0;
int leftSectionSum = col1 - 1 >= 0 ? this.cachedSum[row2][col1 - 1];
return this.cachedSum[row2][col2] - leftSectionSum - topSectionSum + topDiagonalSum;
}
private int fillPreCalculatedSum(Integer[][] matrix, Integer[][] result, int row, int col, Map<Point, Integer> cache) {
Point p = new Point();
p.row = row; p.col = col;
if(cache.contains(p)) {
return cache.get(p);
}
else if(row < 0 || col < 0) {
return 0;
}
int leftSum = col - 1 >= 0 ? this.getPreCalculatedSum(matrix, result row, col - 1, cache) : 0;
int topSum = row - 1 >= 0 ? this.getPreCalculatedSum(matrix, result, row - 1, col, cache) : 0;
int diagonalTopSum = row -1 >= 0 && col - 1 >= 0 ? this.getPreCalculatedSum(matrix, result, row - 1, col - 1, cache) : 0;
int sum = matrix[row][col] + leftSum + topSum - diagonalTopSum;
result[row][col] = sum;
cache.put(p, sum);
return sum;
}
private static class Point {
public int row;
public int col;
}
}
Note that there is optimization for the O(N^2) pre calculation using a 2D segment tree, where changes in the matrix consume O(lg(N)lg(N)) and the same is true for querying. Then there is the addition to do something with lacy calculation by marking parts of the tree dirty and only calculate it when needed... this problem is pretty much open ended
- Chris April 28, 2017