Loading problems...
TL;DR: Traverse every island in grid2 using Depth-First Search (DFS) and verify that every land cell visited corresponds to a land cell in grid1.
The problem asks us to determine the number of "sub-islands" in a binary matrix grid2. A sub-island is defined as a group of connected 1s (land) in grid2 where every single cell in that group also contains a 1 in the corresponding position in grid1. If even one cell of an island in grid2 overlaps with a 0 (water) in grid1, that group is not a sub-island. This is a popular interview question that tests your ability to manipulate matrices and perform graph traversals efficiently.
A naive or brute-force approach might involve a multi-pass strategy. First, one might traverse grid2 to identify all distinct islands and store the coordinates of every cell belonging to each island in a list of lists. After collecting all islands, the algorithm would iterate through each stored island and check the coordinates against grid1.
Pseudo-code:
text1. Create a list `islands` to store sets of coordinates. 2. Iterate through grid2: If cell is 1 and not visited: Perform DFS/BFS to find the full island. Store all (r, c) pairs of this island in `islands`. 3. Initialize count = 0. 4. For each island in `islands`: is_sub_island = True For each (r, c) in island: If grid1[r][c] == 0: is_sub_island = False Break If is_sub_island is True: count++ 5. Return count.
Time Complexity: because we visit nodes a constant number of times. Space Complexity: to store the coordinates of all islands explicitly.
Why it is suboptimal: While the time complexity is technically linear with respect to the grid size, the space complexity is higher than necessary because we explicitly store island coordinates. Furthermore, this approach requires two distinct phases (collection and validation), which adds unnecessary code complexity. In an interview, we want to validate the sub-island property during the traversal to save space and keep the logic concise.
The key insight for LeetCode 1905 is that we can validate the "sub-island" condition simultaneously while traversing the island in grid2. We do not need to store coordinates.
The condition for a sub-island is strict: ALL cells in the grid2 island must be land in grid1. This implies that if we encounter a single cell in the current grid2 island where grid1[r][c] is water (0), the entire island is disqualified.
However, a crucial detail often missed is that we cannot simply stop the traversal (short-circuit) when we find an invalid cell. We must continue the DFS to mark the entire island in grid2 as visited. If we stop early, the unvisited parts of the invalid island will be discovered later by the main loop, erroneously treated as a new island, and potentially counted.
Visualizing the Algorithm:
Imagine the recursion tree expanding from the first cell of an island in grid2. As the DFS flows into neighbor cells (up, down, left, right), it effectively "colors" the island to mark it as processed. Each node in this recursion tree performs a local check: "Is there land at this position in grid1?" The results of these checks are aggregated. If the local check fails or any child node reports a failure, the root of the recursion tree knows this island is not a sub-island.

Let's trace the execution for a specific island in grid2:
1 at grid2[0][0].dfs(0, 0).grid1 is 1. If grid1[0][0] is 0, we flag this island as invalid (but continue traversing).grid2[0][0] as 0 to mark it as visited.(0, 1): It's a 1. Recurse dfs(0, 1).(1, 0): It's a 0. Stop branch.dfs(0, 1) encounters a cell where grid1 is 0. It returns false.dfs(0, 0) receives this false.(0, 0) itself was valid, the false from the neighbor propagates up.1s in this component to 0. The final result is returned to the main loop. If true, count increases.The algorithm relies on the property of Connected Components. By definition, a DFS starting at an unvisited node will visit every node reachable from (i.e., the entire island) exactly once, provided we mark nodes as visited.
The invariant we maintain is:
Our DFS implements this logical conjunction (AND operation). If any node fails the check (grid1[r][c] == 0), the conjunction becomes false. By visiting every node in the component, we ensure that the check is exhaustive for that island. By marking nodes as visited immediately, we ensure termination and prevent cycles. Thus, the count is strictly the number of components satisfying the invariant.
cppclass Solution { public: int m, n; // DFS returns true if the connected component is a valid sub-island bool dfs(vector<vector<int>>& grid1, vector<vector<int>>& grid2, int r, int c) { // Boundary checks and water check if (r < 0 || r >= m || c < 0 || c >= n || grid2[r][c] == 0) { return true; } // Mark as visited in grid2 to avoid cycles and recounting grid2[r][c] = 0; // Check validity for the current cell bool isSub = (grid1[r][c] == 1); // Recurse in all 4 directions. // IMPORTANT: We must NOT use short-circuit operators (&&). // We need to traverse the entire island to mark all cells as visited. bool up = dfs(grid1, grid2, r - 1, c); bool down = dfs(grid1, grid2, r + 1, c); bool left = dfs(grid1, grid2, r, c - 1); bool right = dfs(grid1, grid2, r, c + 1); // The island is valid only if the current cell is valid AND all parts are valid return isSub && up && down && left && right; } int countSubIslands(vector<vector<int>>& grid1, vector<vector<int>>& grid2) { m = grid1.size(); n = grid1[0].size(); int count = 0; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { // If we find an unvisited land cell in grid2 if (grid2[i][j] == 1) { if (dfs(grid1, grid2, i, j)) { count++; } } } } return count; } };
javaclass Solution { private int m; private int n; public int countSubIslands(int[][] grid1, int[][] grid2) { m = grid1.length; n = grid1[0].length; int count = 0; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { // Start DFS if we find unvisited land in grid2 if (grid2[i][j] == 1) { // If the entire island is valid, increment count if (dfs(grid1, grid2, i, j)) { count++; } } } } return count; } private boolean dfs(int[][] grid1, int[][] grid2, int r, int c) { // Base case: check boundaries or if cell is water/visited if (r < 0 || r >= m || c < 0 || c >= n || grid2[r][c] == 0) { return true; } // Mark current cell as visited grid2[r][c] = 0; // Determine if current cell is valid (must be land in grid1) boolean isCurrentValid = (grid1[r][c] == 1); // Visit all neighbors. // We use bitwise AND (&) or separate boolean variables to ensure // DFS executes for ALL directions. Logical AND (&&) would short-circuit. boolean up = dfs(grid1, grid2, r - 1, c); boolean down = dfs(grid1, grid2, r + 1, c); boolean left = dfs(grid1, grid2, r, c - 1); boolean right = dfs(grid1, grid2, r, c + 1); return isCurrentValid && up && down && left && right; } }
pythonclass Solution: def countSubIslands(self, grid1: List[List[int]], grid2: List[List[int]]) -> int: m, n = len(grid1), len(grid1[0]) def dfs(r, c): # Base case: out of bounds or water/visited if r < 0 or r >= m or c < 0 or c >= n or grid2[r][c] == 0: return True # Mark as visited grid2[r][c] = 0 # Check if this cell is valid (must be land in grid1) is_valid = (grid1[r][c] == 1) # Recurse all 4 directions # We must execute all DFS calls to ensure the whole island is marked visited. res1 = dfs(r + 1, c) res2 = dfs(r - 1, c) res3 = dfs(r, c + 1) res4 = dfs(r, c - 1) # Return True only if current cell and all connected parts are valid return is_valid and res1 and res2 and res3 and res4 count = 0 for i in range(m): for j in range(n): if grid2[i][j] == 1: if dfs(i, j): count += 1 return count
Time Complexity:
We traverse the entire grid structure. Each cell in grid2 is visited at most once by the main loop and at most once by the DFS function (constant number of operations per cell). Therefore, the time complexity is linear with respect to the total number of cells.
Space Complexity:
In the worst case, if grid2 consists of a single large island (e.g., a snake pattern filling the grid), the recursion stack for DFS can grow to depth. We modify grid2 in-place to track visited status, so we do not use auxiliary storage for a visited array.
The Graph DFS - Connected Components pattern is fundamental for many grid problems.
Why did I get a Wrong Answer even though my logic seems correct?
return dfs(up) && dfs(down)...) inside the recursion.false, the subsequent conditions are not evaluated. This stops the DFS from visiting the rest of the island.1s. The main loop will find them later, treat them as a new island, and potentially count them incorrectly.Why am I getting an Index Out of Bounds error?
grid2[r][c] before verifying that r and c are within the valid range and .[0, m)[0, n)Why is my solution modifying the input grid?
1 to 0 in grid2 to mark visited nodes.visited set or matrix.