Problem Statement in English

You’re given a 2D grid of characters. You have to determine if there is a cycle in the grid. A cycle is a path of length 4 or more in the grid that starts and ends at the same cell and consists of the same character.

If there is a cycle, return true. Otherwise, return false.


Approach

If you think about it, the grid can be thought of as a grid and a loop can be thought of as a component in a graph.

Thus, we can use union find to solve this problem.

We will iterate through the grid and for each cell, we will check its right and down neighbors. If they are the same character, we will union them.

If they are already in the same component, then we have found a cycle and we can return true. If we finish iterating through the grid and we haven’t found any cycles, we can return false.

There’s a neat little trick to encode the 2D grid into a 1D array. We can use the formula index = (row * number_of_columns) + column to encode the 2D coordinates into a single integer. This will help us in the union find implementation.

A catch here is that we only need to check the right and down neighbors to avoid checking the same pair of cells multiple times. This will incorectly identify cycles as we will be checking the same pair of cells multiple times.

And we’re done!


Solution in Python


class Solution:
    def containsCycle(self, grid: List[List[str]]) -> bool:
        m = len(grid)
        n = len(grid[0])

        parent = [i for i in range(m * n)]
        rank = defaultdict(int)

        directions = [(0, 1), (1, 0)]

        def encode(x, y):
            return (x * n) + y

        def find(i):
            root = i
            while parent[root] != root:
                root = parent[root]

            while parent[i] != root:
                next_node = parent[i]
                parent[i] = root
                i = next_node
            return root

        def union(n1, n2):
            p1 = find(n1)
            p2 = find(n2)

            if p1 == p2:
                return True

            if rank[p1] > rank[p2]:
                parent[p2] = p1
            elif rank[p1] > rank[p2]:
                parent[p1] = p2
            else:
                parent[p1] = p2
                rank[p2] += 1

            return False

        for i in range(m):
            for j in range(n):
                for dx, dy in directions:
                    nx, ny = i + dx, j + dy

                    if 0 <= nx < m and 0 <= ny < n and grid[i][j] == grid[nx][ny]:
                        c1, c2 = encode(i, j), encode(nx, ny)
                        if union(c1, c2):
                            return True

        return False

Complexity

  • Time: $O(m \times n)$
    Since we iterate through the grid once.

  • Space: $O(m \times n)$
    Since we use a union find data structure to store the parent and rank of each cell.


And we are done.