Algorithm (DFS)
- Use regular DFS to find all the connected components for the grid. For each component compute all the its symmetrical variants. This could be done by switching coordinate or rotating the grid.
- Normalize all the components to the upper left corner. This step is necessary since otherwise equivalent components become difficult to identify as each component may live is a different coordinate chart.
- At this moment, we should have for each component 8 equivalent representations in the same coordinate chart. We now pick the representative to be the smallest one in the natural(lexicographical) ordering.
- Then we use a set to get the answer.
- Total complexity is $O(N^3\log(N))$.
Code
class Solution {
public:
int numDistinctIslands2(vector<vector<int>>& grid) {
const auto dr = array<int, 4>{0, 0, 1, -1};
const auto dc = array<int, 4>{1, -1, 0, 0};
auto flipud = [](vector<vector<int>> x) {
std::reverse(begin(x), end(x));
return x;
};
auto fliplr = [](vector<vector<int>> x) {
for (auto& row : x)
std::reverse(begin(row), end(row));
return x;
};
auto inbound = [](int r, int c, const vector<vector<int>>& data) {
const int R = size(data);
const int C = size(data[0]);
return r >= 0 and r < R and c >= 0 and c < C;
};
auto rot90 = [](vector<vector<int>> x) {
const int R = size(x), C = size(x[0]);
auto result = vector<vector<int>>(C, vector<int>(R, 0));
for (int c = 0; c < C; ++c)
for (int r = 0; r < R; ++r)
result[c][R - 1 - r] = x[r][c];
return result;
};
const auto [labeled_grid, cc_cnt] = [&] {
const int R = size(grid), C = size(grid[0]);
auto self = grid;
auto visited = vector<vector<bool>>(R, vector<bool>(C, false));
auto acc = 0;
auto dfs = rec([&](auto&& dfs, int r, int c, int label) -> void {
visited[r][c] = true;
for (int i = 0; i < 4; ++i)
if (const auto [nr, nc] = tuple(r + dr[i], c + dc[i]);
inbound(nr, nc, self) and not visited[nr][nc] and self[nr][nc] == self[r][c])
dfs(nr, nc, label);
self[r][c] = label;
});
for (int r = 0; r < R; ++r)
for (int c = 0; c < C; ++c) {
if (not visited[r][c] and self[r][c] == 1)
dfs(r, c, acc++);
else if (not visited[r][c] and self[r][c] == 0)
dfs(r, c, -1);
}
return tuple(self, acc);
}();
auto connected_components = [&](const vector<vector<int>>& data) {
const int R = size(data), C = size(data[0]);
auto self = vector<vector<array<int, 2>>>(cc_cnt, vector<array<int, 2>>{});
auto visited = vector<vector<bool>>(R, vector<bool>(C, false));
auto dfs = rec([&](auto&& dfs, int r, int c) -> void {
visited[r][c] = true;
for (int i = 0; i < 4; ++i)
if (const auto [nr, nc] = tuple(r + dr[i], c + dc[i]);
inbound(nr, nc, data) and not visited[nr][nc] and data[nr][nc] == data[r][c])
dfs(nr, nc);
self[data[r][c]].emplace_back(array<int, 2>{r, c});
});
for (int r = 0; r < R; ++r)
for (int c = 0; c < C; ++c)
if (not visited[r][c] and data[r][c] != -1)
dfs(r, c);
return self;
};
auto standardize = [&](vector<array<int, 2>> x) {
const auto [min_x, min_y] = tuple{
(*min_element(begin(x), end(x), [](auto& x, auto& y) { return x[0] < y[0]; }))[0],
(*min_element(begin(x), end(x), [](auto& x, auto& y) { return x[1] < y[1]; }))[1]
};
for (auto& coord : x) (coord[0] -= min_x,
coord[1] -= min_y);
return x;
};
const auto all_connected_components = [&](vector<vector<vector<array<int, 2>>>> self = {}) {
self.resize(cc_cnt);
const auto data = vector<vector<vector<array<int, 2>>>>{
connected_components(labeled_grid),
connected_components(fliplr(labeled_grid)),
connected_components(flipud(labeled_grid)),
connected_components(rot90(labeled_grid)),
connected_components(fliplr(rot90(labeled_grid))),
connected_components(flipud(rot90(labeled_grid))),
connected_components(rot90(rot90(labeled_grid))),
connected_components(rot90(rot90(rot90(labeled_grid))))};
for (const auto& x : data)
for (int i = 0; i < size(x); ++i)
self[i].push_back(standardize(x[i]));
for (auto& x : self)
std::sort(begin(x), end(x));
return self;
}();
const auto solution = [&](set<vector<array<int, 2>>> acc = {}) {
for (auto x : all_connected_components)
acc.insert(x[0]);
return size(acc);
}();
return solution;
}
};