1. Find the largest square whose sum does not exceed a threshold.
Implement maxSquareSide(matrix, threshold). matrix is a rectangular array with 1 through 500 rows and columns; every entry and threshold is a non-negative safe integer. Return the greatest side length k for which at least one contiguous k × k submatrix has a sum less than or equal to threshold; return 0 if no 1 × 1 square qualifies. Do not mutate the matrix, and reject ragged rows or non-integer values. The contract permits duplicate values and requires exact integer arithmetic within the safe-integer range. Example: for [[1,1,3,2,4,3,2],[1,1,3,2,4,3,2],[1,1,3,2,4,3,2]] with threshold 4, return 2. Aim for O(rows × columns × log(min(rows,columns))) time or better using standard JavaScript.
I would build a 2D prefix-sum table with BigInt so every tested square sum is exact. Then I binary-search the side length k from 1 to min(rows, columns). For each k, I scan all possible k × k squares and get each sum in O(1) from the prefix table. Because all values are non-negative, feasibility is monotone, so binary search is valid. The total time is O(rows × columns × log(min(rows, columns))) with O(rows × columns) auxiliary space.
See the Code while reading this explanation.
We need to find the largest square inside the matrix whose total is at most the threshold. The square must use consecutive rows and columns. We return only its side length. We do not change the input matrix. I first build a table that lets me get any square sum quickly. Then I binary-search the possible side lengths. This works because every matrix value is non-negative, so if a larger square works, a smaller square inside it also works.
- Should I reject rows with different lengths? Yes. The contract says the matrix must be rectangular.
- Can matrix values or the threshold be negative? No. They are non-negative safe integers.
- Should the sum calculations remain exact even when many safe integers are added together? Yes. The shown solution uses BigInt for the prefix sums.
The input is a rectangular matrix with 1 through 500 rows and 1 through 500 columns, plus a non-negative threshold. Every matrix entry and the threshold is a safe integer. We return the greatest side length k for which at least one contiguous k × k square has sum at most the threshold. If no 1 × 1 square qualifies, we return 0. Ragged rows and non-integer values are rejected.
I create a prefix table P with one extra row and one extra column. P[r][c] stores the sum of the rectangle from matrix[0][0] through matrix[r-1][c-1]. The table uses BigInt so the sums stay exact. The original matrix is never modified.
For the example matrix [[1,1,3,2,4,3,2],[1,1,3,2,4,3,2],[1,1,3,2,4,3,2]], the prefix table is: 0 0 0 0 0 0 0 0 0 1 2 5 7 11 14 16 0 2 4 10 14 22 28 32 0 3 6 15 21 33 42 48
The possible side lengths are 1 through min(rows, columns). In the example, that is 1 through 3. I keep low, high, and best. For each midpoint k, I call hasSquare(k), which checks whether any k × k square has sum at most the threshold.
The key property is monotonicity. If a k × k square is feasible, any smaller square contained inside it has a sum no larger because all entries are non-negative. So after a feasible k, I can search larger sizes. After an infeasible k, I can discard all larger sizes.
Start with low = 1, high = 3, and best = 0. The midpoint is k = 2. The first 2 × 2 square starts at (0,0). Its sum is P[2][2] - P[0][2] - P[2][0] + P[0][0] = 4 - 0 - 0 + 0 = 4. Since 4 ≤ 4, hasSquare(2) returns true immediately. We set best = 2 and low = 3.
Now k = 3. The five possible 3 × 3 squares start at (0,0), (0,1), (0,2), (0,3), and (0,4). Their sums are 15, 18, 27, 27, and 27. None is at most 4, so hasSquare(3) returns false. We set high = 2. Now low = 3 and high = 2, so the binary search stops and returns best = 2.
The prefix table gives the exact sum of every square that hasSquare(k) tests. The helper returns true exactly when at least one square of side k meets the threshold. Feasibility is monotone because all entries are non-negative. The binary search keeps best as the largest feasible size found so far and removes only sizes that cannot contain a better answer. Therefore the final best is the greatest valid side length.
The code validates the matrix dimensions, rectangular shape, entries, and threshold. It then builds the BigInt prefix table. The helper hasSquare(k) scans every possible k × k square and returns true as soon as one qualifies. The outer binary search records k and moves right after success. It moves left after failure. When low becomes greater than high, the function returns best.
Building the prefix table takes O(rows × columns) time. One feasibility test checks at most O(rows × columns) positions, and each square sum takes O(1). Binary search performs O(log(min(rows, columns))) feasibility tests. Total time is O(rows × columns × log(min(rows, columns))). Auxiliary space is O(rows × columns). Important cases are no qualifying 1 × 1 cell, all zeros, one row or one column, duplicate values, ragged rows, and non-integer values.
The key insight is to combine 2D prefix sums with binary search on the answer. The prefix table makes each k × k sum an O(1) inclusion-exclusion calculation. The central invariant is that best is the largest feasible side length found so far, and the remaining binary-search interval contains the only sizes that could still change the answer. Binary search is safe because all entries are non-negative. If a square of side k is feasible, every smaller square contained inside it is also feasible. If k is infeasible, every larger size is infeasible too.
function maxSquareSide(matrix, threshold) {
// Validate the number of rows before reading matrix[0].
if (!Array.isArray(matrix) || matrix.length < 1 || matrix.length > 500) {
throw new TypeError('matrix must have 1 through 500 rows');
}
// Validate the threshold from the problem contract.
if (!Number.isSafeInteger(threshold) || threshold < 0) {
throw new TypeError('threshold must be a non-negative safe integer');
}
// Validate the first row and establish the required column count.
if (!Array.isArray(matrix[0]) || matrix[0].length < 1 || matrix[0].length > 500) {
throw new TypeError('matrix must have 1 through 500 columns');
}
const rows = matrix.length;
const cols = matrix[0].length;
// Reject ragged rows and invalid values without mutating the matrix.
for (const row of matrix) {
if (!Array.isArray(row) || row.length !== cols) {
throw new TypeError('matrix must be rectangular');
}
for (const value of row) {
if (!Number.isSafeInteger(value) || value < 0) {
throw new TypeError('matrix values must be non-negative safe integers');
}
}
}
// Use BigInt prefix sums so all accumulated sums remain exact.
// The extra zero row and zero column simplify inclusion-exclusion.
const prefix = Array.from({ length: rows + 1 }, () => Array(cols + 1).fill(0n));
// Build the 2D prefix table without changing matrix.
for (let r = 1; r <= rows; r++) {
let rowSum = 0n;
for (let c = 1; c <= cols; c++) {
rowSum += BigInt(matrix[r - 1][c - 1]);
prefix[r][c] = prefix[r - 1][c] + rowSum;
}
}
// Convert once so every sum comparison is BigInt-to-BigInt.
const limit = BigInt(threshold);
// Test whether at least one k × k square satisfies the threshold.
function hasSquare(k) {
for (let r = k; r <= rows; r++) {
for (let c = k; c <= cols; c++) {
// Inclusion-exclusion gives this square sum in O(1).
const sum = prefix[r][c] - prefix[r - k][c] - prefix[r][c - k] + prefix[r - k][c - k];
// Stop immediately after the first qualifying square.
if (sum <= limit) {
return true;
}
}
}
return false;
}
// Binary-search the inclusive side-length range.
let low = 1;
let high = Math.min(rows, cols);
let best = 0;
while (low <= high) {
const mid = low + Math.floor((high - low) / 2);
if (hasSquare(mid)) {
// mid is feasible. Record it and try a larger size.
best = mid;
low = mid + 1;
} else {
// mid is infeasible. By monotonicity, discard all larger sizes.
high = mid - 1;
}
}
// If no 1 × 1 square qualifies, best is still 0.
return best;
}
// Run the same example shown in the diagram.
const matrix = [
[1, 1, 3, 2, 4, 3, 2],
[1, 1, 3, 2, 4, 3, 2],
[1, 1, 3, 2, 4, 3, 2],
];
const threshold = 4;
console.log(maxSquareSide(matrix, threshold)); // 2Let R be the number of rows and C be the number of columns. Building the prefix table takes O(R × C) time. For one candidate size k, hasSquare(k) may inspect O(R × C) positions, and each square sum takes O(1) time. Binary search tries O(log(min(R, C))) side lengths. So total time is O(R × C × log(min(R, C))). The prefix table stores O(R × C) BigInt values, so auxiliary space is O(R × C).
This pattern is useful when a program needs many fast sum queries on rectangular grid areas. Examples include image-processing grids, heatmaps, game maps, and analytics tables. A 2D prefix sum makes each region-sum query fast. Binary search can be added when the answer is a size and the valid sizes form a monotone true-or-false range.
This problem checks whether you can combine two ideas correctly. You need 2D prefix sums for fast square-sum queries. You also need to recognize that non-negative values make feasibility monotone, which allows binary search on the side length. The interviewer can evaluate boundary handling, early-return reasoning, exact JavaScript arithmetic with BigInt, input validation, and whether you can state the O(R × C × log(min(R, C))) time and O(R × C) auxiliary space correctly.
- Using Number for accumulated prefix sums and forgetting that adding many safe integers can exceed the exact Number integer range. The shown solution uses BigInt for accumulated sums.
- Using the wrong plus and minus signs in the prefix-sum inclusion-exclusion formula.
- Continuing to process more squares after hasSquare(k) already found a qualifying square, even though the implementation returns immediately.
- Using binary search without explaining why non-negative entries make feasibility monotone.
- Forgetting to reject ragged rows or non-integer values.
- Claiming O(1) auxiliary space even though the prefix table grows with the matrix.
Explain the monotone property before writing the binary search. Say that because every value is non-negative, a feasible k × k square contains smaller squares whose sums cannot be larger. That directly explains why searching the side length with binary search is safe.









