检查操作是否合法

标签: 数组 枚举 矩阵

难度: Medium

给你一个下标从 0 开始的 8 x 8 网格 board ,其中 board[r][c] 表示游戏棋盘上的格子 (r, c) 。棋盘上空格用 '.' 表示,白色格子用 'W' 表示,黑色格子用 'B' 表示。

游戏中每次操作步骤为:选择一个空格子,将它变成你正在执行的颜色(要么白色,要么黑色)。但是,合法 操作必须满足:涂色后这个格子是 好线段的一个端点 (好线段可以是水平的,竖直的或者是对角线)。

好线段 指的是一个包含 三个或者更多格子(包含端点格子)的线段,线段两个端点格子为 同一种颜色 ,且中间剩余格子的颜色都为 另一种颜色 (线段上不能有任何空格子)。你可以在下图找到好线段的例子:

给你两个整数 rMove 和 cMove 以及一个字符 color ,表示你正在执行操作的颜色(白或者黑),如果将格子 (rMove, cMove) 变成颜色 color 后,是一个 合法 操作,那么返回 true ,如果不是合法操作返回 false 。

示例 1:

输入:board = [[".",".",".","B",".",".",".","."],[".",".",".","W",".",".",".","."],[".",".",".","W",".",".",".","."],[".",".",".","W",".",".",".","."],["W","B","B",".","W","W","W","B"],[".",".",".","B",".",".",".","."],[".",".",".","B",".",".",".","."],[".",".",".","W",".",".",".","."]], rMove = 4, cMove = 3, color = "B"
输出:true
解释:'.','W' 和 'B' 分别用颜色蓝色,白色和黑色表示。格子 (rMove, cMove) 用 'X' 标记。
以选中格子为端点的两个好线段在上图中用红色矩形标注出来了。

示例 2:

输入:board = [[".",".",".",".",".",".",".","."],[".","B",".",".","W",".",".","."],[".",".","W",".",".",".",".","."],[".",".",".","W","B",".",".","."],[".",".",".",".",".",".",".","."],[".",".",".",".","B","W",".","."],[".",".",".",".",".",".","W","."],[".",".",".",".",".",".",".","B"]], rMove = 4, cMove = 4, color = "W"
输出:false
解释:虽然选中格子涂色后,棋盘上产生了好线段,但选中格子是作为中间格子,没有产生以选中格子为端点的好线段。

提示:

  • board.length == board[r].length == 8
  • 0 <= rMove, cMove < 8
  • board[rMove][cMove] == '.'
  • color 要么是 'B' 要么是 'W' 。

Submission

运行时间: 29 ms

内存: 16.0 MB

class Solution:
    def checkMove(self, board: List[List[str]], rMove: int, cMove: int, color: str) -> bool:
        dx = [-1,-1,0,1,1,1,0,-1]
        dy = [0,1,1,1,0,-1,-1,-1]
        board[rMove][cMove] = color
        for i in range(8):
            x = rMove+ dx[i]
            y = cMove+dy[i]
            length = 1
            while 0<= x < 8 and 0<=y <8:
                length+=1
                if board[x][y] == '.':
                    break
                if board[x][y] == color:
                    if length < 3:
                        break
                    return True
                x += dx[i]
                y+= dy[i]
        return False
            
        # def legal(r, c, color):
        #     dx = [-1,-1,0,1,1,1,0,-1]
        #     dy = [0,1,1,1,0,-1,-1,-1]

Explain

题解的思路是在棋盘上模拟将选定的格子涂成指定颜色后,检查它是否成为一个好线段的端点。为此,代码首先模拟涂色,然后从该点向八个方向(上、下、左、右、四个对角线方向)检查。通过逐步扩展到下一个格子,代码寻找相同颜色的端点,同时确保中间没有空格,并且中间的格子颜色为另一种。如果在任何方向上找到至少三个格子组成的线段,且端点与初始颜色相同,则返回true,否则检查下一个方向。如果所有方向都不满足条件,则返回false。

时间复杂度: O(1)

空间复杂度: O(1)

# 增加注释的题解代码
class Solution:
    def checkMove(self, board: List[List[str]], rMove: int, cMove: int, color: str) -> bool:
        # 定义8个方向的偏移量
        dx = [-1, -1, 0, 1, 1, 1, 0, -1]
        dy = [0, 1, 1, 1, 0, -1, -1, -1]
        # 在选定位置模拟涂色
        board[rMove][cMove] = color
        # 检查每个方向
        for i in range(8):
            x = rMove + dx[i]
            y = cMove + dy[i]
            length = 1
            # 沿当前方向扩展,检查线段
            while 0 <= x < 8 and 0 <= y < 8:
                length += 1
                if board[x][y] == '.':
                    break
                if board[x][y] == color:
                    if length < 3:
                        break
                    return True
                x += dx[i]
                y += dy[i]
        return False

Explore

在题解的代码中,我们通过循环迭代检查每个方向上的格子。当遇到一个格子的颜色与初始颜色相同时,检查此时的线段长度是否至少为3(包括起点和当前格子)。如果长度小于3,则即刻终止当前方向的检查。此外,如果遇到空格('.'),循环也会终止。这样的设计确保了不会漏掉检查线段中间所有格子必须为相反颜色的条件。

`dx`和`dy`数组代表了从一个格子出发可能的八个方向的偏移量,分别对应上、下、左、右和四个对角线方向。这确保了在棋盘上任何位置的所有可能方向都被覆盖。因此,这两个数组能够完整地代表棋盘上所有可能形成好线段的方向。

在代码中,`length`变量确实会一直增加直到遇到一个与初始颜色相同的格子或者空格。如果中途遇到相同颜色的格子,会检查此时的`length`是否小于3,如果是则终止当前方向的检查。这种处理方式并不会造成逻辑错误,因为只有当长度至少为3(包括两端的相同颜色格子)时,才会返回`true`。这确保了不会错误地认定连续的相同颜色格子为合法线段。

代码在找到第一个符合条件的好线段后会立即返回`true`,因此不会继续检查其他方向。这意味着代码只需找到一个合法线段即可确定操作是合法的,而不需要关心是否存在多个符合条件的方向。这种处理逻辑是符合题目要求的,旨在检查至少存在一个合法线段即可。