You are given a 2D matrix of integers called mat
with dimensions m x n
, and an integer threshold
.
Your task is to find the maximum possible side length of a square (with sides parallel to the axes) such that the sum of all elements inside the square is less than or equal to threshold
.
mat
.threshold
.Constraints:
1 ≤ m, n ≤ 300
0 ≤ mat[i][j] ≤ 10,000
0 ≤ threshold ≤ 10^5
At first glance, a brute-force approach would be to check every possible square in the matrix, for every possible size, and compute the sum of its elements. However, this would be extremely slow for large matrices, since there are many possible squares and each sum could take O(k2) time to compute for a square of size k.
To optimize, we need a way to quickly compute the sum of any sub-square in constant time. This leads us to the concept of a prefix sum matrix, which allows us to get the sum of any rectangular submatrix in O(1) time after preprocessing. With this, we can efficiently check all possible squares for a given side length.
To further optimize, instead of checking all possible square sizes one by one, we can use binary search to find the largest valid side length. This reduces the number of checks we need to make.
The solution involves two main optimizations: prefix sum for fast area queries, and binary search for the answer.
prefix
of size (m+1) x (n+1), where prefix[i][j]
is the sum of elements in the submatrix from (0,0) to (i-1,j-1).
sum = prefix[r2+1][c2+1] - prefix[r1][c2+1] - prefix[r2+1][c1] + prefix[r1][c1]
min(m, n)
.k
, check if there exists a square of size k x k whose sum is ≤ threshold.k
, slide a k x k window over the matrix.
Consider mat = [[1,1,3,2,4,3,2],[1,1,3,2,4,3,2],[1,1,3,2,4,3,2]]
and threshold = 4
.
By combining prefix sums for fast area queries and binary search for the answer, we efficiently solve the problem of finding the largest square with sum ≤ threshold. The key insight is to preprocess the matrix for O(1) sum queries and to avoid checking every possible square size sequentially. This approach is both elegant and highly efficient, making it suitable for large input sizes.
class Solution:
def maxSideLength(self, mat, threshold):
m, n = len(mat), len(mat[0])
# Build prefix sum matrix
prefix = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m):
for j in range(n):
prefix[i+1][j+1] = mat[i][j] + prefix[i][j+1] + prefix[i+1][j] - prefix[i][j]
def possible(k):
for i in range(m - k + 1):
for j in range(n - k + 1):
total = prefix[i+k][j+k] - prefix[i][j+k] - prefix[i+k][j] + prefix[i][j]
if total <= threshold:
return True
return False
left, right = 0, min(m, n)
ans = 0
while left <= right:
mid = (left + right) // 2
if possible(mid):
ans = mid
left = mid + 1
else:
right = mid - 1
return ans
class Solution {
public:
int maxSideLength(vector<vector<int>>& mat, int threshold) {
int m = mat.size(), n = mat[0].size();
vector<vector<int>> prefix(m+1, vector<int>(n+1, 0));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
prefix[i+1][j+1] = mat[i][j] + prefix[i][j+1] + prefix[i+1][j] - prefix[i][j];
}
}
auto possible = [&](int k) {
for (int i = 0; i + k <= m; ++i) {
for (int j = 0; j + k <= n; ++j) {
int total = prefix[i+k][j+k] - prefix[i][j+k] - prefix[i+k][j] + prefix[i][j];
if (total <= threshold)
return true;
}
}
return false;
};
int left = 0, right = min(m, n), ans = 0;
while (left <= right) {
int mid = (left + right) / 2;
if (possible(mid)) {
ans = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return ans;
}
};
class Solution {
public int maxSideLength(int[][] mat, int threshold) {
int m = mat.length, n = mat[0].length;
int[][] prefix = new int[m + 1][n + 1];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
prefix[i + 1][j + 1] = mat[i][j] + prefix[i][j + 1] + prefix[i + 1][j] - prefix[i][j];
}
}
int left = 0, right = Math.min(m, n), ans = 0;
while (left <= right) {
int mid = (left + right) / 2;
if (possible(prefix, m, n, mid, threshold)) {
ans = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return ans;
}
private boolean possible(int[][] prefix, int m, int n, int k, int threshold) {
for (int i = 0; i + k <= m; i++) {
for (int j = 0; j + k <= n; j++) {
int total = prefix[i + k][j + k] - prefix[i][j + k] - prefix[i + k][j] + prefix[i][j];
if (total <= threshold)
return true;
}
}
return false;
}
}
var maxSideLength = function(mat, threshold) {
const m = mat.length, n = mat[0].length;
const prefix = Array.from({length: m + 1}, () => Array(n + 1).fill(0));
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
prefix[i+1][j+1] = mat[i][j] + prefix[i][j+1] + prefix[i+1][j] - prefix[i][j];
}
}
function possible(k) {
for (let i = 0; i + k <= m; i++) {
for (let j = 0; j + k <= n; j++) {
let total = prefix[i+k][j+k] - prefix[i][j+k] - prefix[i+k][j] + prefix[i][j];
if (total <= threshold) return true;
}
}
return false;
}
let left = 0, right = Math.min(m, n), ans = 0;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (possible(mid)) {
ans = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return ans;
};