-
Notifications
You must be signed in to change notification settings - Fork 820
/
NumberOfDistinctIslandsII.java
154 lines (145 loc) · 5.28 KB
/
NumberOfDistinctIslandsII.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* (C) 2024 YourCompanyName */
package depth_first_search;
import java.util.*;
import java.util.stream.Collectors;
/**
* Created by gouthamvidyapradhan on 27/04/2018. Given a non-empty 2D array grid of 0's and 1's, an
* island is a group of 1's (representing land) connected 4-directionally (horizontal or vertical.)
* You may assume all four edges of the grid are surrounded by water.
*
* <p>Count the number of distinct islands. An island is considered to be the same as another if
* they have the same shape, or have the same shape after rotation (90, 180, or 270 degrees only) or
* reflection (left/right direction or up/down direction).
*
* <p>Example 1: 11000 10000 00001 00011 Given the above grid map, return 1.
*
* <p>Notice that: 11 1 and 1 11 are considered same island shapes. Because if we make a 180 degrees
* clockwise rotation on the first island, then two islands will have the same shapes. Example 2:
* 11100 10001 01001 01110 Given the above grid map, return 2.
*
* <p>Here are the two distinct islands: 111 1 and 1 1
*
* <p>Notice that: 111 1 and 1 111 are considered same island shapes. Because if we flip the first
* array in the up/down direction, then they have the same shapes. Note: The length of each
* dimension in the given grid does not exceed 50.
*
* <p>Solution: General idea is to get the co-ordinates of each shape using dfs and rotate/reflect
* each point in a shape to transform each shape to a new possible shape (there are 8 possible
* shapes after rotation and reflection). Sort the new coordinates of each transformed shape and
* reduce each shape to a canonical key. Use a hash-set to count total number of such keys.
*
* <p>Some background on rotation and reflection: ------------------------------------------- Rotate
* co-ordinates using formula [x′y′]=[[cosθ -sinθ], [sinθ cosθ]] [x y] where θ = {0, 90, 180, 270}
* There are 4 possible rotation points and for each rotation point obtain the reflection on each x
* and y axis. Rotation and reflection results in total of 8 points such as (x, y), (-x, y), (x,
* -y), (-x, -y), (y, x), (-y, x), (y, -x) and (-y, -x).
*/
public class NumberOfDistinctIslandsII {
private final int[] R = {0, 1, 0, -1};
private final int[] C = {1, 0, -1, 0};
private boolean[][] done;
class Point implements Comparable<Point> {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public int compareTo(Point o) {
if (this.x == o.x) {
return Integer.compare(this.y, o.y);
}
return Integer.compare(this.x, o.x);
}
}
/**
* Main method
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
int[][] grid = {{1, 1, 0, 0, 0}, {1, 0, 0, 0, 0}, {0, 0, 0, 0, 1}, {0, 0, 0, 1, 1}};
System.out.println(new NumberOfDistinctIslandsII().numDistinctIslands2(grid));
}
public int numDistinctIslands2(int[][] grid) {
List<List<Point>> shapes = new ArrayList<>();
done = new boolean[grid.length][grid[0].length];
Set<String> islands = new HashSet<>();
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (!done[i][j] && grid[i][j] == 1) {
List<Point> points = new ArrayList<>();
dfs(i, j, grid, points);
shapes.add(points);
}
}
}
for (List<Point> shape : shapes) {
List<List<Point>> eightShapes = rotateAndReflect(shape);
islands.add(genKey(eightShapes));
}
return islands.size();
}
/**
* Generate a canonical key
*
* @param eighShapes
* @return
*/
private String genKey(List<List<Point>> eighShapes) {
List<String> keys = new ArrayList<>();
for (List<Point> shape : eighShapes) {
Collections.sort(shape);
Point first = shape.get(0);
keys.add(
shape
.stream()
.map(s -> new Point(s.x - first.x, s.y - first.y))
.map(p -> p.x + ":" + p.y)
.collect(Collectors.joining(",")));
}
Collections.sort(keys);
return keys.get(0);
}
/**
* Rotate and reflect a given shape to 8 possible shapes
*
* @param shape
* @return
*/
private List<List<Point>> rotateAndReflect(List<Point> shape) {
Map<Integer, List<Point>> map = new HashMap<>();
for (int i = 0; i < 8; i++) {
map.put(i, new ArrayList<>());
}
for (Point point : shape) {
map.get(0).add(new Point(point.x, point.y));
map.get(1).add(new Point(-point.x, point.y));
map.get(2).add(new Point(point.x, -point.y));
map.get(3).add(new Point(-point.x, -point.y));
map.get(4).add(new Point(point.y, point.x));
map.get(5).add(new Point(-point.y, point.x));
map.get(6).add(new Point(point.y, -point.x));
map.get(7).add(new Point(-point.y, -point.x));
}
return new ArrayList<>(map.values());
}
private void dfs(int r, int c, int[][] grid, List<Point> points) {
done[r][c] = true;
points.add(new Point(c, r));
for (int i = 0; i < 4; i++) {
int newR = r + R[i];
int newC = c + C[i];
if (newR >= 0
&& newC >= 0
&& newR < grid.length
&& newC < grid[0].length
&& grid[newR][newC] == 1
&& !done[newR][newC]) {
dfs(newR, newC, grid, points);
}
}
}
}