Before attempting this problem, you should be comfortable with:
We simulate the candy crush game by repeatedly performing three operations: find all candies that need to be crushed (three or more adjacent same-colored candies horizontally or vertically), crush them by setting their values to zero, and then drop remaining candies down to fill the gaps. We repeat this cycle until no more candies can be crushed.
find function that scans the board for groups of three or more adjacent candies (horizontally and vertically) and stores their positions in a set.crush function that sets all positions in the crushed set to 0.drop function that, for each column, moves all non-zero candies down to fill the gaps left by crushed candies (using a lowest-zero pointer technique).find, crush, and drop until find returns an empty set.class Solution:
def candyCrush(self, board: List[List[int]]) -> List[List[int]]:
m, n = len(board), len(board[0])
def find():
crushed_set = set()
# Check vertically adjacent candies
for r in range(1, m - 1):
for c in range(n):
if board[r][c] == 0:
continue
if board[r][c] == board[r - 1][c] == board[r + 1][c]:
crushed_set.add((r, c))
crushed_set.add((r - 1, c))
crushed_set.add((r + 1, c))
# Check horizontally adjacent candies
for r in range(m):
for c in range(1, n - 1):
if board[r][c] == 0:
continue
if board[r][c] == board[r][c - 1] == board[r][c + 1]:
crushed_set.add((r, c))
crushed_set.add((r, c - 1))
crushed_set.add((r, c + 1))
return crushed_set
# Set the value of each candies to be crushed as 0
def crush(crushed_set):
for (r, c) in crushed_set:
board[r][c] = 0
def drop():
for c in range(n):
lowest_zero = -1
# Iterate over each column
for r in range(m - 1, -1, -1):
if board[r][c] == 0:
lowest_zero = max(lowest_zero, r)
# Swap current non-zero candy with the lowest zero.
elif lowest_zero >= 0:
board[r][c], board[lowest_zero][c] = board[lowest_zero][c], board[r][c]
lowest_zero -= 1
# Continue with the three steps until we can no longer find any crushable candies.
crushed_set = find()
while crushed_set:
crush(crushed_set)
drop()
crushed_set = find()
return boardWhere is the size of the grid
board
Instead of using a separate set to track crushed candies, we can mark them in-place by negating their values. This allows us to identify candies to crush while still being able to check for matching (using absolute values). After marking, we convert all negative values to 0. This reduces space usage compared to maintaining a separate set.
0.drop function to move non-zero candies down in each column.drop until no candies are marked for crushing.class Solution:
def candyCrush(self, board: List[List[int]]) -> List[List[int]]:
m, n = len(board), len(board[0])
def find_and_crush():
complete = True
# Check vertically adjacent candies
for r in range(1, m - 1):
for c in range(n):
if board[r][c] == 0:
continue
if abs(board[r][c]) == abs(board[r - 1][c]) == abs(board[r + 1][c]):
board[r][c] = -abs(board[r][c])
board[r - 1][c] = -abs(board[r - 1][c])
board[r + 1][c] = -abs(board[r + 1][c])
complete = False
# Check horizontally adjacent candies
for r in range(m):
for c in range(1, n - 1):
if board[r][c] == 0:
continue
if abs(board[r][c]) == abs(board[r][c - 1]) == abs(board[r][c + 1]):
board[r][c] = -abs(board[r][c])
board[r][c - 1] = -abs(board[r][c - 1])
board[r][c + 1] = -abs(board[r][c + 1])
complete = False
# Set the value of each candies to be crushed as 0
for r in range(m):
for c in range(n):
if board[r][c] < 0:
board[r][c] = 0
return complete
def drop():
for c in range(n):
lowest_zero = -1
# Iterate over each column
for r in range(m - 1, -1, -1):
if board[r][c] == 0:
lowest_zero = max(lowest_zero, r)
# Swap current non-zero candy with the lowest zero.
elif lowest_zero >= 0:
board[r][c], board[lowest_zero][c] = board[lowest_zero][c], board[r][c]
lowest_zero -= 1
# Continue with the three steps until we can no longer find any crushable candies.
while not find_and_crush():
drop()
return boardWhere is the size of the grid
board
Groups can be longer than three candies. Checking only the center of a triplet misses candies at the ends of longer sequences. The solution must mark all candies in contiguous groups of three or more.
If you crush candies immediately upon finding a match, you may miss overlapping horizontal and vertical groups. All crushable candies must be identified first, then crushed together.
When dropping candies, you must process each column from bottom to top, moving non-zero candies down to fill gaps. A common mistake is shifting candies horizontally or not properly tracking the lowest available position.
# Wrong: dropping row by row instead of column by column
for r in range(m - 1, -1, -1):
for c in range(n): # Should iterate columns in outer loop