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:

1
2
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:

1
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.length
  • n == board[i].length
  • 3 <= m, n <= 50
  • 1 <= 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

  1. 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).
  2. Repeat until no more candies can be crushed.
  3. Return the stable board.

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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 }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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)