764. Largest Plus Sign
Problem Description
You have an n x n
binary grid that initially contains all 1
s, except for certain positions specified in the mines
array. Each element mines[i] = [xi, yi]
indicates that grid[xi][yi] = 0
.
Your task is to find the largest possible "plus sign" made of 1
s in this grid. A plus sign is defined as:
- It has a center cell containing
1
- It has four arms extending up, down, left, and right from the center
- All cells in the arms must contain
1
s - The "order" of a plus sign is
k
, where the arms have lengthk - 1
For example, a plus sign of order 3 would look like:
1 1 1 1 1 1 1 1 1
The plus sign must be axis-aligned (arms extend horizontally and vertically only). The cells beyond the plus sign can be either 0
s or 1
s - they don't affect the validity of the plus sign.
Return the order of the largest plus sign that can be found in the grid. If no plus sign exists (all cells are 0
s), return 0
.
Example scenarios:
- In a 5x5 grid with no mines, the largest plus sign would have order 3 (center at position [2,2] with arms extending 2 cells in each direction)
- If mines block certain positions, the maximum plus sign order might be smaller
- A single
1
surrounded by0
s forms a plus sign of order 1
Intuition
The key insight is that for any cell to be the center of a plus sign of order k
, it needs to have at least k-1
consecutive 1
s extending in all four directions (up, down, left, right). The maximum order of a plus sign centered at any cell is limited by the shortest arm it can extend.
Think of it this way: if a cell can extend 5 cells to the left, 3 cells to the right, 4 cells up, and 6 cells down, the maximum plus sign centered at this cell would have order min(5, 3, 4, 6) = 3
.
This naturally leads to a dynamic programming approach where for each cell, we need to calculate:
- How many consecutive
1
s can we extend to the left (including itself)? - How many consecutive
1
s can we extend to the right? - How many consecutive
1
s can we extend upward? - How many consecutive
1
s can we extend downward?
The minimum of these four values gives us the maximum order of a plus sign centered at that cell.
The clever part of the solution is how it computes these four directional values efficiently. Instead of checking each cell independently four times, we can:
- Process each row from left to right to compute left-extension values
- Process each row from right to left to compute right-extension values
- Process each column from top to bottom to compute up-extension values
- Process each column from bottom to top to compute down-extension values
By using running counters that reset to 0 when we hit a mine and increment when we see a 1
, we can compute all these values in a single pass through the grid. The dp[i][j]
value is continuously updated to maintain the minimum of all four directional extensions, ultimately giving us the maximum plus sign order possible at each position.
Learn more about Dynamic Programming patterns.
Solution Approach
The solution uses a dynamic programming approach with clever simultaneous computation of all four directional extensions.
Step 1: Initialize the DP table
dp = [[n] * n for _ in range(n)]
We initialize a 2D array dp
where each cell starts with value n
(the maximum possible arm length). This represents the potential maximum plus sign order at each position.
Step 2: Mark the mines
for x, y in mines: dp[x][y] = 0
Set all mine positions to 0 since no plus sign can be centered at a mine.
Step 3: Process all four directions simultaneously
for i in range(n):
left = right = up = down = 0
for j, k in zip(range(n), reversed(range(n))):
For each row/column index i
, we process:
- Row
i
from left to right (using indexj
from 0 to n-1) - Row
i
from right to left (using indexk
from n-1 to 0) - Column
i
from top to bottom (using indexj
) - Column
i
from bottom to top (using indexk
)
Step 4: Count consecutive 1s in each direction
left = left + 1 if dp[i][j] else 0 right = right + 1 if dp[i][k] else 0 up = up + 1 if dp[j][i] else 0 down = down + 1 if dp[k][i] else 0
For each direction, we maintain a running counter:
- If the current cell is not a mine (value > 0), increment the counter
- If it's a mine (value = 0), reset the counter to 0
Step 5: Update DP values
dp[i][j] = min(dp[i][j], left)
dp[i][k] = min(dp[i][k], right)
dp[j][i] = min(dp[j][i], up)
dp[k][i] = min(dp[k][i], down)
For each cell, we keep the minimum of its current value and the newly computed directional extension. After processing all directions, dp[i][j]
contains the maximum order of a plus sign that can be centered at position (i, j)
.
Step 6: Find the maximum
return max(max(v) for v in dp)
The answer is the maximum value in the entire dp
table, representing the largest plus sign order possible in the grid.
Time Complexity: O(n²)
- we visit each cell a constant number of times
Space Complexity: O(n²)
- for the dp table
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with n = 5
and mines = [[4, 2]]
:
Initial Grid (1s everywhere except mine at [4,2]):
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1
Step 1: Initialize DP table with n=5:
dp = [[5, 5, 5, 5, 5], [5, 5, 5, 5, 5], [5, 5, 5, 5, 5], [5, 5, 5, 5, 5], [5, 5, 5, 5, 5]]
Step 2: Mark mine at [4,2]:
dp = [[5, 5, 5, 5, 5], [5, 5, 5, 5, 5], [5, 5, 5, 5, 5], [5, 5, 5, 5, 5], [5, 5, 0, 5, 5]]
Step 3: Process each i from 0 to 4:
When i = 0 (processing row 0 horizontally and column 0 vertically):
- Processing row 0 left-to-right: left counts = [1, 2, 3, 4, 5]
- Processing row 0 right-to-left: right counts = [5, 4, 3, 2, 1]
- Processing column 0 top-to-bottom: up counts = [1, 2, 3, 4, 5]
- Processing column 0 bottom-to-top: down counts = [5, 4, 3, 2, 1]
After updates:
dp = [[1, 2, 3, 4, 5], [2, 5, 5, 5, 5], [3, 5, 5, 5, 5], [4, 5, 5, 5, 5], [5, 5, 0, 5, 5]]
When i = 4 (processing row 4 with the mine):
-
Processing row 4 left-to-right:
- j=0: left=1, dp[4][0] = min(5, 1) = 1
- j=1: left=2, dp[4][1] = min(2, 2) = 2
- j=2: left=0 (mine), dp[4][2] = min(0, 0) = 0
- j=3: left=1, dp[4][3] = min(1, 1) = 1
- j=4: left=2, dp[4][4] = min(1, 2) = 1
-
Processing row 4 right-to-left:
- k=4: right=1, dp[4][4] = min(1, 1) = 1
- k=3: right=2, dp[4][3] = min(1, 2) = 1
- k=2: right=0 (mine), dp[4][2] = min(0, 0) = 0
- k=1: right=1, dp[4][1] = min(2, 1) = 1
- k=0: right=2, dp[4][0] = min(1, 2) = 1
Final DP table after all processing:
dp = [[1, 1, 1, 1, 1], [1, 2, 2, 2, 1], [1, 2, 3, 2, 1], [1, 2, 2, 2, 1], [1, 1, 0, 1, 1]]
Result: The maximum value in dp is 3 (at position [2,2]).
This means we can form a plus sign of order 3 centered at [2,2]:
1 1 1 1 1 1 1 1 1
The mine at [4,2] prevents any larger plus signs from forming, as it blocks the downward arm of any plus sign centered above it in column 2.
Solution Implementation
1class Solution:
2 def orderOfLargestPlusSign(self, n: int, mines: List[List[int]]) -> int:
3 # Initialize a 2D array where each cell stores the maximum possible arm length
4 # of a plus sign centered at that position
5 max_arm_length = [[n] * n for _ in range(n)]
6
7 # Mark cells with mines as 0 (no plus sign possible there)
8 for mine_row, mine_col in mines:
9 max_arm_length[mine_row][mine_col] = 0
10
11 # For each row and column, calculate the consecutive 1s in all four directions
12 for i in range(n):
13 # Variables to track consecutive 1s in each direction
14 consecutive_left = 0
15 consecutive_right = 0
16 consecutive_up = 0
17 consecutive_down = 0
18
19 # Process all four directions simultaneously
20 for j, k in zip(range(n), reversed(range(n))):
21 # Left direction: process row i from left to right
22 consecutive_left = consecutive_left + 1 if max_arm_length[i][j] != 0 else 0
23
24 # Right direction: process row i from right to left
25 consecutive_right = consecutive_right + 1 if max_arm_length[i][k] != 0 else 0
26
27 # Up direction: process column i from top to bottom
28 consecutive_up = consecutive_up + 1 if max_arm_length[j][i] != 0 else 0
29
30 # Down direction: process column i from bottom to top
31 consecutive_down = consecutive_down + 1 if max_arm_length[k][i] != 0 else 0
32
33 # Update each cell with the minimum arm length from its current value
34 # and the newly calculated consecutive count in that direction
35 max_arm_length[i][j] = min(max_arm_length[i][j], consecutive_left)
36 max_arm_length[i][k] = min(max_arm_length[i][k], consecutive_right)
37 max_arm_length[j][i] = min(max_arm_length[j][i], consecutive_up)
38 max_arm_length[k][i] = min(max_arm_length[k][i], consecutive_down)
39
40 # Return the maximum arm length found in the entire grid
41 # This represents the order of the largest plus sign
42 return max(max(row) for row in max_arm_length)
43
1class Solution {
2 public int orderOfLargestPlusSign(int n, int[][] mines) {
3 // Initialize dp array where dp[i][j] represents the maximum arm length of plus sign centered at (i,j)
4 // Initially set all values to n (maximum possible arm length)
5 int[][] dp = new int[n][n];
6 for (int[] row : dp) {
7 Arrays.fill(row, n);
8 }
9
10 // Mark mines positions with 0 (no plus sign can be formed at mine positions)
11 for (int[] mine : mines) {
12 dp[mine[0]][mine[1]] = 0;
13 }
14
15 // Calculate the minimum arm length for each position from all four directions
16 for (int i = 0; i < n; i++) {
17 // Track consecutive 1s count from each direction
18 int leftCount = 0; // Count of consecutive 1s from left
19 int rightCount = 0; // Count of consecutive 1s from right
20 int upCount = 0; // Count of consecutive 1s from up
21 int downCount = 0; // Count of consecutive 1s from down
22
23 // Process row i from left to right and right to left simultaneously
24 // Process column i from up to down and down to up simultaneously
25 for (int j = 0, k = n - 1; j < n; j++, k--) {
26 // Left direction: process row i from left to right
27 leftCount = dp[i][j] > 0 ? leftCount + 1 : 0;
28 // Right direction: process row i from right to left
29 rightCount = dp[i][k] > 0 ? rightCount + 1 : 0;
30 // Up direction: process column i from top to bottom
31 upCount = dp[j][i] > 0 ? upCount + 1 : 0;
32 // Down direction: process column i from bottom to top
33 downCount = dp[k][i] > 0 ? downCount + 1 : 0;
34
35 // Update dp values with minimum arm length from all directions seen so far
36 dp[i][j] = Math.min(dp[i][j], leftCount);
37 dp[i][k] = Math.min(dp[i][k], rightCount);
38 dp[j][i] = Math.min(dp[j][i], upCount);
39 dp[k][i] = Math.min(dp[k][i], downCount);
40 }
41 }
42
43 // Find and return the maximum value in the dp array
44 // This represents the order of the largest plus sign
45 return Arrays.stream(dp)
46 .flatMapToInt(Arrays::stream)
47 .max()
48 .getAsInt();
49 }
50}
51
1class Solution {
2public:
3 int orderOfLargestPlusSign(int n, vector<vector<int>>& mines) {
4 // Initialize dp array with maximum possible value n for each cell
5 // dp[i][j] will store the maximum radius of plus sign centered at (i, j)
6 vector<vector<int>> dp(n, vector<int>(n, n));
7
8 // Mark all mine positions as 0 (cannot form plus sign at mines)
9 for (auto& mine : mines) {
10 dp[mine[0]][mine[1]] = 0;
11 }
12
13 // Calculate the maximum plus sign radius for each cell by considering
14 // consecutive 1s in all four directions
15 for (int i = 0; i < n; ++i) {
16 // Variables to track consecutive 1s in each direction
17 int leftCount = 0; // Consecutive 1s from left
18 int rightCount = 0; // Consecutive 1s from right
19 int upCount = 0; // Consecutive 1s from up
20 int downCount = 0; // Consecutive 1s from down
21
22 // Process row i (left to right and right to left) and column i (up to down and down to up)
23 for (int j = 0, k = n - 1; j < n; ++j, --k) {
24 // Left direction: process row i from left to right
25 leftCount = dp[i][j] ? leftCount + 1 : 0;
26 dp[i][j] = min(dp[i][j], leftCount);
27
28 // Right direction: process row i from right to left
29 rightCount = dp[i][k] ? rightCount + 1 : 0;
30 dp[i][k] = min(dp[i][k], rightCount);
31
32 // Up direction: process column i from top to bottom
33 upCount = dp[j][i] ? upCount + 1 : 0;
34 dp[j][i] = min(dp[j][i], upCount);
35
36 // Down direction: process column i from bottom to top
37 downCount = dp[k][i] ? downCount + 1 : 0;
38 dp[k][i] = min(dp[k][i], downCount);
39 }
40 }
41
42 // Find the maximum value in the dp array (largest plus sign radius)
43 int maxRadius = 0;
44 for (auto& row : dp) {
45 maxRadius = max(maxRadius, *max_element(row.begin(), row.end()));
46 }
47
48 return maxRadius;
49 }
50};
51
1function orderOfLargestPlusSign(n: number, mines: number[][]): number {
2 // Initialize dp array with maximum possible value n for each cell
3 // dp[i][j] will store the maximum radius of plus sign centered at (i, j)
4 const dp: number[][] = Array(n).fill(0).map(() => Array(n).fill(n));
5
6 // Mark all mine positions as 0 (cannot form plus sign at mines)
7 for (const mine of mines) {
8 dp[mine[0]][mine[1]] = 0;
9 }
10
11 // Calculate the maximum plus sign radius for each cell by considering
12 // consecutive 1s in all four directions
13 for (let i = 0; i < n; i++) {
14 // Variables to track consecutive 1s in each direction
15 let leftCount = 0; // Consecutive 1s from left
16 let rightCount = 0; // Consecutive 1s from right
17 let upCount = 0; // Consecutive 1s from up
18 let downCount = 0; // Consecutive 1s from down
19
20 // Process row i (left to right and right to left) and column i (up to down and down to up)
21 for (let j = 0, k = n - 1; j < n; j++, k--) {
22 // Left direction: process row i from left to right
23 leftCount = dp[i][j] ? leftCount + 1 : 0;
24 dp[i][j] = Math.min(dp[i][j], leftCount);
25
26 // Right direction: process row i from right to left
27 rightCount = dp[i][k] ? rightCount + 1 : 0;
28 dp[i][k] = Math.min(dp[i][k], rightCount);
29
30 // Up direction: process column i from top to bottom
31 upCount = dp[j][i] ? upCount + 1 : 0;
32 dp[j][i] = Math.min(dp[j][i], upCount);
33
34 // Down direction: process column i from bottom to top
35 downCount = dp[k][i] ? downCount + 1 : 0;
36 dp[k][i] = Math.min(dp[k][i], downCount);
37 }
38 }
39
40 // Find the maximum value in the dp array (largest plus sign radius)
41 let maxRadius = 0;
42 for (const row of dp) {
43 maxRadius = Math.max(maxRadius, Math.max(...row));
44 }
45
46 return maxRadius;
47}
48
Time and Space Complexity
Time Complexity: O(n²)
The algorithm uses a nested loop structure where:
- The outer loop runs
n
times (iterating through each row/column indexi
) - The inner loop runs
n
times for each outer loop iteration (iterating through indexj
andk
simultaneously) - Inside the inner loop, all operations (comparisons, assignments, min operations) are
O(1)
- The initial setup of the
dp
array takesO(n²)
time - Setting mines in the
dp
array takesO(m)
time wherem
is the number of mines, butm ≤ n²
- The final operation to find the maximum value requires traversing the entire
dp
array, which isO(n²)
Therefore, the overall time complexity is O(n²) + O(m) + O(n²) + O(n²) = O(n²)
Space Complexity: O(n²)
The space usage includes:
- A 2D
dp
array of sizen × n
, which requiresO(n²)
space - Four auxiliary variables (
left
,right
,up
,down
) which requireO(1)
space - Loop variables and other temporary variables require
O(1)
space
Therefore, the overall space complexity is O(n²)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Order Definition
A common mistake is confusing the "order" of a plus sign with its arm length. The order k
means the arms extend k-1
cells from the center, not k
cells. For example:
- Order 1: Just the center cell (no arms extending)
- Order 2: Center + 1 cell in each direction
- Order 3: Center + 2 cells in each direction
Solution: Remember that the order equals the number of consecutive 1s in each direction (including the center), which is why the code counts consecutive 1s rather than arm lengths.
2. Incorrect Initialization of the DP Table
Some might initialize the DP table with 0s or 1s instead of n
. This would break the algorithm because:
- We need to track the minimum arm length across all four directions
- Starting with 0 would prevent any meaningful updates
- Starting with 1 would limit the maximum possible plus sign size
Solution: Initialize with n
(the maximum possible value) so that the min()
operations can properly reduce it based on actual constraints.
3. Processing Directions Sequentially Instead of Simultaneously
A tempting but incorrect approach would be to process each direction in separate loops:
# WRONG APPROACH
for i in range(n):
for j in range(n):
# Calculate left arm
for i in range(n):
for j in range(n):
# Calculate right arm
# etc.
This would require multiple passes and complicate the logic for maintaining consecutive counts.
Solution: The clever use of zip(range(n), reversed(range(n)))
allows processing opposite directions in a single pass, maintaining the consecutive count efficiently.
4. Forgetting to Reset Consecutive Counts at Mines
When encountering a mine (0 value), the consecutive count must reset to 0, not continue or decrement. Missing this reset would incorrectly calculate arm lengths across mines:
# WRONG: Just decrementing or not updating consecutive_left = consecutive_left - 1 if max_arm_length[i][j] == 0 else consecutive_left + 1 # CORRECT: Reset to 0 at mines consecutive_left = consecutive_left + 1 if max_arm_length[i][j] != 0 else 0
Solution: Always reset the counter to 0 when encountering a mine to ensure arms don't "jump over" blocked cells.
5. Overwriting Values Before They're Used
Since we're updating the same DP table we're reading from, there's a risk of using already-modified values. However, the algorithm cleverly avoids this by:
- Only updating with
min()
operations (never increasing values) - Processing each direction's updates to different cells in the correct order
Solution: The algorithm's design inherently avoids this issue, but if modifying the approach, be careful about the order of reads and writes to ensure you're using the correct values for calculations.
How does merge sort divide the problem into subproblems?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!