Candy Crush
Problem
This question is about implementing a basic elimination algorithm for Candy Crush.
Given an m x n integer array board representing the grid of candy where board[i][j] represents the type of candy. A value of board[i][j] == 0 represents that the cell is empty.
The given board represents the state of the game following the player's move. Now, you need to restore the board to a stable state by crushing candies according to the following rules:
- If three or more candies of the same type are adjacent vertically or horizontally, crush them all at the same time - these positions become empty.
- After crushing all candies simultaneously, if an empty space on the board has candies on top of itself, then these candies will drop until they hit a candy or bottom at the same time. No new candies will drop outside the top boundary.
- After the above steps, there may exist more candies that can be crushed. If so, you need to repeat the above steps.
- If there does not exist more candies that can be crushed (i.e., the board is stable), then return the current board.
You need to perform the above rules until the board becomes stable, then return the stable board.
Examples
Example 1:

Input: board = [[110,5,112,113,114],[210,211,5,213,214],[310,311,3,313,314],[410,411,412,5,414],[5,1,512,3,3],[610,4,1,613,614],[710,1,2,713,714],[810,1,2,1,1],[1,1,2,2,2],[4,1,4,4,1014]]
Output: [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[110,0,0,0,114],[210,0,0,0,214],[310,0,0,113,314],[410,0,0,213,414],[610,211,112,313,614],[710,311,412,613,714],[810,411,512,713,1014]]
Example 2:
Input: board = [[1,3,5,5,2],[3,4,3,3,1],[3,2,4,5,2],[2,4,4,5,5],[1,4,4,1,1]]
Output: [[1,3,0,0,0],[3,4,0,5,2],[3,2,0,3,1],[2,4,0,5,2],[1,4,3,1,1]]
Constraints:
m == board.lengthn == board[i].length3 <= m, n <= 501 <= board[i][j] <= 2000
Solution
Method 1 – Simulation with Marking and Gravity
Intuition
We repeatedly mark all candies that should be crushed (three or more in a row or column), set them to zero, and then let the candies above fall down (gravity). We repeat this process until the board is stable (no more candies to crush).
Approach
- While the board is not stable:
- Mark all horizontal and vertical groups of three or more same candies for crushing.
- Set all marked candies to zero.
- For each column, move non-zero candies down and fill the rest with zeros (gravity).
- Repeat until no more candies can be crushed.
- Return the stable board.
Code
C++
class Solution {
public:
vector<vector<int>> candyCrush(vector<vector<int>>& board) {
int m = board.size(), n = board[0].size();
bool changed = true;
while (changed) {
changed = false;
// Mark candies to crush
for (int i = 0; i < m; ++i) {
for (int j = 0; j + 2 < n; ++j) {
int v = abs(board[i][j]);
if (v != 0 && v == abs(board[i][j+1]) && v == abs(board[i][j+2])) {
board[i][j] = board[i][j+1] = board[i][j+2] = -v;
changed = true;
}
}
}
for (int j = 0; j < n; ++j) {
for (int i = 0; i + 2 < m; ++i) {
int v = abs(board[i][j]);
if (v != 0 && v == abs(board[i+1][j]) && v == abs(board[i+2][j])) {
board[i][j] = board[i+1][j] = board[i+2][j] = -v;
changed = true;
}
}
}
// Crush
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
if (board[i][j] < 0) board[i][j] = 0;
// Gravity
for (int j = 0; j < n; ++j) {
int r = m - 1;
for (int i = m - 1; i >= 0; --i) {
if (board[i][j] > 0) board[r--][j] = board[i][j];
}
while (r >= 0) board[r--][j] = 0;
}
}
return board;
}
};
Go
func candyCrush(board [][]int) [][]int {
m, n := len(board), len(board[0])
changed := true
for changed {
changed = false
// Mark
for i := 0; i < m; i++ {
for j := 0; j+2 < n; j++ {
v := abs(board[i][j])
if v != 0 && v == abs(board[i][j+1]) && v == abs(board[i][j+2]) {
board[i][j], board[i][j+1], board[i][j+2] = -v, -v, -v
changed = true
}
}
}
for j := 0; j < n; j++ {
for i := 0; i+2 < m; i++ {
v := abs(board[i][j])
if v != 0 && v == abs(board[i+1][j]) && v == abs(board[i+2][j]) {
board[i][j], board[i+1][j], board[i+2][j] = -v, -v, -v
changed = true
}
}
}
// Crush
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if board[i][j] < 0 {
board[i][j] = 0
}
}
}
// Gravity
for j := 0; j < n; j++ {
r := m - 1
for i := m - 1; i >= 0; i-- {
if board[i][j] > 0 {
board[r][j] = board[i][j]
r--
}
}
for r >= 0 {
board[r][j] = 0
r--
}
}
}
return board
}
func abs(x int) int { if x < 0 { return -x }; return x }
Java
class Solution {
public int[][] candyCrush(int[][] board) {
int m = board.length, n = board[0].length;
boolean changed = true;
while (changed) {
changed = false;
// Mark
for (int i = 0; i < m; i++) {
for (int j = 0; j + 2 < n; j++) {
int v = Math.abs(board[i][j]);
if (v != 0 && v == Math.abs(board[i][j+1]) && v == Math.abs(board[i][j+2])) {
board[i][j] = board[i][j+1] = board[i][j+2] = -v;
changed = true;
}
}
}
for (int j = 0; j < n; j++) {
for (int i = 0; i + 2 < m; i++) {
int v = Math.abs(board[i][j]);
if (v != 0 && v == Math.abs(board[i+1][j]) && v == Math.abs(board[i+2][j])) {
board[i][j] = board[i+1][j] = board[i+2][j] = -v;
changed = true;
}
}
}
// Crush
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
if (board[i][j] < 0) board[i][j] = 0;
// Gravity
for (int j = 0; j < n; j++) {
int r = m - 1;
for (int i = m - 1; i >= 0; i--) {
if (board[i][j] > 0) board[r--][j] = board[i][j];
}
while (r >= 0) board[r--][j] = 0;
}
}
return board;
}
}
Kotlin
class Solution {
fun candyCrush(board: Array<IntArray>): Array<IntArray> {
val m = board.size
val n = board[0].size
var changed = true
while (changed) {
changed = false
// Mark
for (i in 0 until m) {
for (j in 0 until n - 2) {
val v = kotlin.math.abs(board[i][j])
if (v != 0 && v == kotlin.math.abs(board[i][j+1]) && v == kotlin.math.abs(board[i][j+2])) {
board[i][j] = -v
board[i][j+1] = -v
board[i][j+2] = -v
changed = true
}
}
}
for (j in 0 until n) {
for (i in 0 until m - 2) {
val v = kotlin.math.abs(board[i][j])
if (v != 0 && v == kotlin.math.abs(board[i+1][j]) && v == kotlin.math.abs(board[i+2][j])) {
board[i][j] = -v
board[i+1][j] = -v
board[i+2][j] = -v
changed = true
}
}
}
// Crush
for (i in 0 until m) {
for (j in 0 until n) {
if (board[i][j] < 0) board[i][j] = 0
}
}
// Gravity
for (j in 0 until n) {
var r = m - 1
for (i in m - 1 downTo 0) {
if (board[i][j] > 0) {
board[r][j] = board[i][j]
r--
}
}
while (r >= 0) {
board[r][j] = 0
r--
}
}
}
return board
}
}
Python
class Solution:
def candyCrush(self, board: list[list[int]]) -> list[list[int]]:
m, n = len(board), len(board[0])
changed = True
while changed:
changed = False
# Mark
for i in range(m):
for j in range(n - 2):
v = abs(board[i][j])
if v != 0 and v == abs(board[i][j+1]) and v == abs(board[i][j+2]):
board[i][j] = board[i][j+1] = board[i][j+2] = -v
changed = True
for j in range(n):
for i in range(m - 2):
v = abs(board[i][j])
if v != 0 and v == abs(board[i+1][j]) and v == abs(board[i+2][j]):
board[i][j] = board[i+1][j] = board[i+2][j] = -v
changed = True
# Crush
for i in range(m):
for j in range(n):
if board[i][j] < 0:
board[i][j] = 0
# Gravity
for j in range(n):
r = m - 1
for i in range(m - 1, -1, -1):
if board[i][j] > 0:
board[r][j] = board[i][j]
r -= 1
for i in range(r, -1, -1):
board[i][j] = 0
return board
Rust
impl Solution {
pub fn candy_crush(mut board: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
let m = board.len();
let n = board[0].len();
let mut changed = true;
while changed {
changed = false;
// Mark
for i in 0..m {
for j in 0..n - 2 {
let v = board[i][j].abs();
if v != 0 && v == board[i][j + 1].abs() && v == board[i][j + 2].abs() {
board[i][j] = -v;
board[i][j + 1] = -v;
board[i][j + 2] = -v;
changed = true;
}
}
}
for j in 0..n {
for i in 0..m - 2 {
let v = board[i][j].abs();
if v != 0 && v == board[i + 1][j].abs() && v == board[i + 2][j].abs() {
board[i][j] = -v;
board[i + 1][j] = -v;
board[i + 2][j] = -v;
changed = true;
}
}
}
// Crush
for i in 0..m {
for j in 0..n {
if board[i][j] < 0 {
board[i][j] = 0;
}
}
}
// Gravity
for j in 0..n {
let mut r = m as i32 - 1;
for i in (0..m).rev() {
if board[i][j] > 0 {
board[r as usize][j] = board[i][j];
r -= 1;
}
}
for i in (0..=r).rev() {
board[i as usize][j] = 0;
}
}
}
board
}
}
TypeScript
class Solution {
candyCrush(board: number[][]): number[][] {
const m = board.length, n = board[0].length;
let changed = true;
while (changed) {
changed = false;
// Mark
for (let i = 0; i < m; i++) {
for (let j = 0; j + 2 < n; j++) {
const v = Math.abs(board[i][j]);
if (v !== 0 && v === Math.abs(board[i][j+1]) && v === Math.abs(board[i][j+2])) {
board[i][j] = board[i][j+1] = board[i][j+2] = -v;
changed = true;
}
}
}
for (let j = 0; j < n; j++) {
for (let i = 0; i + 2 < m; i++) {
const v = Math.abs(board[i][j]);
if (v !== 0 && v === Math.abs(board[i+1][j]) && v === Math.abs(board[i+2][j])) {
board[i][j] = board[i+1][j] = board[i+2][j] = -v;
changed = true;
}
}
}
// Crush
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (board[i][j] < 0) board[i][j] = 0;
}
}
// Gravity
for (let j = 0; j < n; j++) {
let r = m - 1;
for (let i = m - 1; i >= 0; i--) {
if (board[i][j] > 0) {
board[r][j] = board[i][j];
r--;
}
}
for (let i = r; i >= 0; i--) {
board[i][j] = 0;
}
}
}
return board;
}
}
Complexity
- ⏰ Time complexity:
O((m*n)^2) - 🧺 Space complexity:
O(1)