Given a 2D grid of characters, your task is to determine if there exists a cycle in the grid. A cycle is a path of length 4 or more that starts and ends at the same cell, moves only up, down, left, or right, and only traverses cells with the same character. Additionally, you cannot immediately revisit the cell you just came from (i.e., no immediate backtracking). Each movement must stay within the bounds of the grid.
grid
.true
if there is at least one such cycle, false
otherwise.At first glance, the problem resembles the classic "cycle detection" in graphs, but applied to a 2D grid where each cell is a node, and edges exist between adjacent (up/down/left/right) cells of the same character.
A naive brute-force solution would try every possible path from every cell, but this is computationally infeasible for large grids. Instead, we realize that the problem can be reduced to cycle detection in an undirected graph, with the twist that we can only move to cells of the same character and must not immediately backtrack.
The standard approach for cycle detection in undirected graphs is to use Depth-First Search (DFS), keeping track of the parent node to avoid trivial cycles. If during DFS we reach a previously visited cell that isn't the parent, and the path length is at least 4, we've found a valid cycle.
Here's how we solve the problem efficiently:
true
.
This approach ensures we do not revisit the same state, and the parent tracking prevents false positives due to immediate backtracking.
Sample Input:
grid = [ ['a', 'a', 'a', 'a'], ['a', 'b', 'b', 'a'], ['a', 'b', 'b', 'a'], ['a', 'a', 'a', 'a'] ]
Step-by-step:
true
as soon as the cycle is detected.Brute-force approach: Trying all possible paths from every cell would be exponential in the number of cells, which is infeasible for large grids.
Optimized DFS approach:
This is efficient enough for the problem's constraints.
The key insight is to model the problem as cycle detection in an undirected graph where each cell is a node, and edges exist only between adjacent cells of the same character. By using DFS with parent tracking, we efficiently find cycles of length 4 or more without redundant computation. This approach leverages classic graph theory concepts, adapted to the 2D grid setting, making the solution both elegant and performant.
class Solution:
def containsCycle(self, grid):
n, m = len(grid), len(grid[0])
visited = [[False]*m for _ in range(n)]
def dfs(x, y, from_x, from_y, char, depth):
if visited[x][y]:
return depth >= 4
visited[x][y] = True
for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
nx, ny = x+dx, y+dy
if 0<=nx<n and 0<=ny<m and grid[nx][ny]==char:
if (nx,ny) == (from_x, from_y):
continue
if dfs(nx, ny, x, y, char, depth+1):
return True
return False
for i in range(n):
for j in range(m):
if not visited[i][j]:
if dfs(i, j, -1, -1, grid[i][j], 1):
return True
return False
class Solution {
public:
int n, m;
vector<vector<bool>> visited;
vector<vector<char>> grid;
bool dfs(int x, int y, int px, int py, char ch, int depth) {
if (visited[x][y]) return depth >= 4;
visited[x][y] = true;
int dirs[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
for (int d = 0; d < 4; ++d) {
int nx = x + dirs[d][0], ny = y + dirs[d][1];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && grid[nx][ny] == ch) {
if (nx == px && ny == py) continue;
if (dfs(nx, ny, x, y, ch, depth + 1)) return true;
}
}
return false;
}
bool containsCycle(vector<vector<char>>& g) {
grid = g;
n = grid.size();
m = grid[0].size();
visited.assign(n, vector<bool>(m, false));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (!visited[i][j]) {
if (dfs(i, j, -1, -1, grid[i][j], 1)) return true;
}
}
}
return false;
}
};
class Solution {
int n, m;
boolean[][] visited;
char[][] grid;
boolean dfs(int x, int y, int px, int py, char ch, int depth) {
if (visited[x][y]) return depth >= 4;
visited[x][y] = true;
int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
for (int[] dir : dirs) {
int nx = x + dir[0], ny = y + dir[1];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && grid[nx][ny] == ch) {
if (nx == px && ny == py) continue;
if (dfs(nx, ny, x, y, ch, depth + 1)) return true;
}
}
return false;
}
public boolean containsCycle(char[][] grid) {
this.grid = grid;
n = grid.length;
m = grid[0].length;
visited = new boolean[n][m];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (!visited[i][j]) {
if (dfs(i, j, -1, -1, grid[i][j], 1)) return true;
}
}
}
return false;
}
}
var containsCycle = function(grid) {
const n = grid.length, m = grid[0].length;
const visited = Array.from({length: n}, () => Array(m).fill(false));
function dfs(x, y, px, py, ch, depth) {
if (visited[x][y]) return depth >= 4;
visited[x][y] = true;
for (const [dx, dy] of [[-1,0],[1,0],[0,-1],[0,1]]) {
const nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < n && ny >= 0 && ny < m && grid[nx][ny] === ch) {
if (nx === px && ny === py) continue;
if (dfs(nx, ny, x, y, ch, depth + 1)) return true;
}
}
return false;
}
for (let i = 0; i < n; ++i) {
for (let j = 0; j < m; ++j) {
if (!visited[i][j]) {
if (dfs(i, j, -1, -1, grid[i][j], 1)) return true;
}
}
}
return false;
};