
Problem Statement
In this problem, you are provided with an m x n binary matrix called grid, which represents a map where 1 indicates land and 0 indicates water. An island is a contiguous group of 1s connected either horizontally or vertically but not diagonally. The task is to identify distinct islands in this grid. Distinct islands are islands that have the same shape and can be translated (shifted in any direction without rotation or reflection) to match exactly with another island. The challenge is to find the total number of such distinct islands present in the matrix.
Examples
Example 1
Input:
grid = [[1,1,0,0,0],[1,1,0,0,0],[0,0,0,1,1],[0,0,0,1,1]]
Output:
1
Example 2
Input:
grid = [[1,1,0,1,1],[1,0,0,0,0],[0,0,0,0,1],[1,1,0,1,1]]
Output:
3
Constraints
m == grid.lengthn == grid[i].length1 <= m, n <= 50grid[i][j]is either0or1.
Approach and Intuition
To solve this problem, we have to systematically explore each element of the grid, and when we find a land cell (1), we should perform a depth-first search (DFS) or breadth-first search (BFS) to track the entire island structure. Here's an outlined approach:
- Initialize visitation tracking, typically using a set or matrix to keep track of which cells have been visited.
- Traverse the matrix cell by cell. When you encounter an unvisited
1, you initiate a DFS or BFS from that cell. - For tracking whether two islands are the same, besides marking the visited cells, use a pattern or a path signature strategy. For example, as you perform DFS, you can record the direction moves (up, down, left, right represented perhaps as 'U', 'D', 'L', 'R') relative to the starting cell of the traversal. This string can serve as a unique identifier for the shape and structure of an island.
- Store each unique island signature in a set — since sets automatically handle duplicates, any repeated island shape will not be added again.
- The final count of distinct islands will be the size of this set of unique island signatures.
Key challenges and considerations include:
- Boundary conditions need careful handling to ensure searches do not go out of the grid limits.
- Efficiently differentiating between island shapes and efficiently storing and comparing these shapes.
- Ensuring complete visitation of each potential island part once an island cell is found.
Examples from the prompt:
- In Example 1, the grid forms one island that repeats itself. Thus, despite the island appearing twice, the answer is 1.
- In Example 2, different configurations and disconnected parts mean there are a total of 3 distinct islands.
The constraints (1 <= m, n <= 50) allow for a full exploration of the grid without the need for exceptionally complex optimizations.
Solutions
- Java
class IslandCounter {
private int[][] matrix;
private boolean[][] checked;
private StringBuilder islandShape;
public int countUniqueIslands(int[][] matrix) {
this.matrix = matrix;
this.checked = new boolean[matrix.length][matrix[0].length];
Set<String> uniqueIslands = new HashSet<>();
for (int x = 0; x < matrix.length; x++) {
for (int y = 0; y < matrix[0].length; y++) {
islandShape = new StringBuilder();
explore(x, y, '0');
if (islandShape.length() == 0) {
continue;
}
uniqueIslands.add(islandShape.toString());
}
}
return uniqueIslands.size();
}
private void explore(int x, int y, char direction) {
if (x < 0 || y < 0 || x >= matrix.length || y >= matrix[0].length) {
return;
}
if (checked[x][y] || matrix[x][y] == 0) {
return;
}
checked[x][y] = true;
islandShape.append(direction);
explore(x + 1, y, 'D');
explore(x - 1, y, 'U');
explore(x, y + 1, 'R');
explore(x, y - 1, 'L');
islandShape.append('0');
}
}
The solution provided tackles the problem of determining the number of distinct islands in a 2D grid. Each island is made up of connected land cells (represented by 1s) separated by water cells (represented by 0s). The distinction of each island is based on its shape rather than just the count of cells.
Here is an outline that summarizes the approach implemented in your Java code:
- Initialize a class
IslandCounterwith a private 2D integer arraymatrixfor the grid and a 2D boolean arraycheckedto keep track of the cells that have already been visited. - Declare a
StringBuilderislandShapeto dynamically record the shape of the island during exploration. - Implement the
countUniqueIslandsfunction which:- Takes a 2D integer array
matrix. - Initializes the
checkedarray to ensure all cells are initially set as not visited. - Uses a
HashSetuniqueIslandsto store unique island shapes – uniqueness determined by their navigation path string created during the exploration. - Iterates through each cell in the matrix, using the helper function
exploreto navigate through the island from each unvisited land cell, concatenating directions to theislandShapeto form a unique identifier for the shape of the island. - Adds the completed island shape to the set of unique islands if not seen before.
- Returns the size of
uniqueIslandsset, which corresponds to the number of distinct islands.
- Takes a 2D integer array
- The
explorefunction:- Is a recursive function that receives coordinates
(x, y)and the direction from which it was called. - Base case checks ensure it does not explore out of bounds or revisit cells or traverse water cells.
- Marks the cell as visited and updates the
islandShapewith the direction moved. - Recursively calls itself for the four possible directions (up, down, left, right).
- Upon returning from each recursive call, appends a termination mark to signal the end of that path sequence, aiding in distinguishing between different structures.
- Is a recursive function that receives coordinates
Implement this solution effectively ensures that each distinct shape of the island is recognized, counting only truly unique islands. The inclusion of navigation paths in the shape definition is key to distinguishing between islands of similar sizes but different configurations.
- Python
class Solution:
def countUniqueIslands(self, matrix: List[List[int]]) -> int:
def exploreIsland(x, y, dir):
if x < 0 or y < 0 or x >= len(matrix) or y >= len(matrix[0]):
return
if (x, y) in visited or matrix[x][y] == 0:
return
visited.add((x, y))
island_pattern.append(dir)
exploreIsland(x + 1, y, "D")
exploreIsland(x - 1, y, "U")
exploreIsland(x, y + 1, "R")
exploreIsland(x, y - 1, "L")
island_pattern.append("back")
visited = set()
distinct_islands = set()
for i in range(len(matrix)):
for j in range(len(matrix[0])):
island_pattern = []
exploreIsland(i, j, "start")
if island_pattern:
distinct_islands.add(tuple(island_pattern))
return len(distinct_islands)
The provided code defines a solution for determining the number of distinct islands in a given 2D matrix where islands are represented with 1s and water with 0s.
Ensure the matrix is passed to the countUniqueIslands function. The function employs a nested helper function named exploreIsland, which uses a depth-first search (DFS) approach to traverse the island and capture its unique shape. This is accomplished using directions as markers during the traversal, allowing for a distinctive pattern representation of each island.
- The algorithm starts by initializing a
visitedset to keep track of visited grid cells and adistinct_islandsset to store unique patterns of islands. - Iteration through the matrix cells occurs, and for each unvisited cell marked by a
1,exploreIslandis invoked. - The
exploreIslandperforms DFS from the current cell in all four cardinal directions (Down, Up, Right, Left), appending the move direction to theisland_patternlist and marking the position as visited. - Upon retracing steps in the DFS ('backtracking'), the word "back" is added to the pattern.
- Once an island pattern traversal is completed, its tuple representation (to ensure immutability and hashability) is added to
distinct_islandsif it's not already present. - The main function returns the count of distinct island patterns found by converting the length of the
distinct_islandsset to an integer.
In summary, the function uniquely identifies islands based on their shape and relative cell positions within the matrix, disregarding identical islands' different locations. This Python solution is efficient, leveraging set operations for quick look-ups and ensuring no redundant pattern storage through unique tuple checks.