这是一道检查网格中是否存在合法括号字符串路径的问题。我会逐步分析并给出实现。

问题分析

我们需要从 (0,0) 到 (m-1, n-1) 找一条路径,使得路径上的字符组成的括号字符串是合法的。

关键难点:网格大小可能达到 100×100,直接 BFS/DFS 会超时,需要状态剪枝。

核心思路

由于括号需要匹配,任何时候 左括号数 >= 右括号数,且最终左右括号数相等。路径长度固定为 m+n-1,所以:

· 长度必须是偶数(否则不能完全匹配)
· 左右括号数量各占一半

状态设计:(i, j, balance) 表示到达 (i,j) 时未匹配的左括号数

重要观察:

· balance 最大不会超过路径长度的一半(最多 (m+n-1)/2)
· 同一 (i,j,balance) 只需访问一次

代码实现

```java
class Solution {
    public boolean hasValidPath(char[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        
        // 路径长度必须是偶数
        if ((m + n - 1) % 2 != 0) return false;
        
        // 起点和终点必须是括号匹配的可能起点/终点
        if (grid[0][0] == ')' || grid[m-1][n-1] == '(') return false;
        
        // 使用三维visited数组进行剪枝
        Boolean[][][] memo = new Boolean[m][n][(m+n)/2 + 1];
        
        return dfs(grid, 0, 0, 0, memo);
    }
    
    private boolean dfs(char[][] grid, int i, int j, int balance, Boolean[][][] memo) {
        int m = grid.length;
        int n = grid[0].length;
        
        // 更新balance
        if (grid[i][j] == '(') {
            balance++;
        } else {
            balance--;
        }
        
        // 剪枝:balance不能为负,且不能超过剩余路径能匹配的最大值
        if (balance < 0) return false;
        if (balance > (m + n - i - j - 1)) return false;
        
        // 到达终点
        if (i == m-1 && j == n-1) {
            return balance == 0;
        }
        
        // 检查缓存
        if (memo[i][j][balance] != null) {
            return memo[i][j][balance];
        }
        
        // 向下或向右移动
        boolean result = false;
        if (i + 1 < m) {
            result = dfs(grid, i+1, j, balance, memo);
        }
        if (!result && j + 1 < n) {
            result = dfs(grid, i, j+1, balance, memo);
        }
        
        return memo[i][j][balance] = result;
    }
}
```

关键优化点

1. 长度奇偶性检查:提前排除不可能的情况
2. 起点终点检查:起点不能是 ),终点不能是 (
3. balance上限:balance 最大不会超过剩余未访问格子数(这些格子最多能提供的左括号数)
4. 记忆化搜索:避免重复状态

复杂度分析

· 时间复杂度:O(m × n × (m+n)),但实际剪枝后远小于此
· 空间复杂度:O(m × n × (m+n))

这个解法能通过 LeetCode 的测试用例。

 

更多推荐