日期:2026年9月29日
LeetCode 題目連結:2267. Check if There Is a Valid Parentheses String Path
解題想法
困難題。題目給一只有 $($ 及 $)$ 的二維陣列 $grid$,假設陣列的大小為 $m \times n$,判斷這個陣列是否可以找到符合以下的條件路徑:
- 括號成對
- 從左上角 $(0, 0)$ 出發,走到右下角 $(m-1, n-1)$。
- 只能往下走或往右走
- 總步數 $m + n - 1$ 如果是奇數
- $grid[0][0] == ')'$
- $grid[m-1][n-1] == '('$
- 更新左括號減右括號數量
- 如果 memo 之中有已經算過的結果,直接回傳
- 剪枝,如果右括號比左括號多,不符合規則,回傳 False。
- 遞迴出口,抵達終點,檢查左、右括號是否一樣多。
- 遞迴,向下走。如果向下走可以找到合法的路徑,記錄 $(r, c, balance)$ 的狀態為 True,回傳 True。
- 遞迴,向右走。如果向右走可以找到合法的路徑,記錄 $(r, c, balance)$ 的狀態為 True,回傳 True。
- 如果前兩個遞迴沒有找到合法的路徑,記錄 $(r, c, balance)$ 的狀態為 False,回傳 False。
Python 程式碼
使用 map 記錄狀態。Runtime: 7 ms, beats 96.00%. Memory: 29.92 MB, beats 72.00%.
class Solution:
def hasValidPath(self, grid: list[list[str]]) -> bool:
m, n = len(grid), len(grid[0]) # m 列、n 欄
# 特例,如果總步數 m + n - 1 是奇數,不可能平衡
if (m + n - 1) % 2 == 1: return False
# 特例,grid[0][0] 是 ),不可能平衡
if grid[0][0] == ')': return False
# 特例,grid[m-1][n-1] 是 (,不可能平衡
if grid[m-1][n-1] == '()': return False
# 記憶化 dfs,不使用 functools.cache
# 代入坐標 (r, c),左括號減右括號數量 balance
memo = dict() # (r, c, balance): True or False
def dfs(r, c, balance):
# 更新左括號減右括號數量
if grid[r][c] == '(':
balance += 1
else:
balance -= 1
# 如果 memo 之中有已經算過的結果,直接回傳
if (r, c, balance) in memo:
return memo[r, c, balance]
# 剪枝,如果右括號比左括號多,不符合規則,回傳 False
if balance < 0: return False
# 遞迴出口,抵達終點,檢查左、右括號是否一樣多
if r == m-1 and c == n-1:
return balance == 0
# 遞迴,向下走
if r < m-1 and dfs(r+1, c, balance):
memo[r, c, balance] = True
return True
# 遞迴,向右走
if c < n-1 and dfs(r, c+1, balance):
memo[r, c, balance] = True
return True
# 預設回傳 False
memo[r, c, balance] = False
return False
# 呼叫 dfs 求答案
return dfs(0, 0, 0)
使用 functools.cache。Runtime: 7 ms, beats 96.00%. Memory: 30.04 MB, beats 71.00%.
from functools import cache
class Solution:
def hasValidPath(self, grid: list[list[str]]) -> bool:
m, n = len(grid), len(grid[0]) # m 列、n 欄
# 特例,如果總步數 m + n - 1 是奇數,不可能平衡
if (m + n - 1) % 2 == 1: return False
# 特例,grid[0][0] 是 ),不可能平衡
if grid[0][0] == ')': return False
# 特例,grid[m-1][n-1] 是 (,不可能平衡
if grid[m-1][n-1] == '()': return False
# 記憶化 dfs,使用 functools.cache
# 代入坐標 (r, c),左括號減右括號數量 balance
@cache
def dfs(r, c, balance):
# 更新左括號減右括號數量
if grid[r][c] == '(':
balance += 1
else:
balance -= 1
# 剪枝,如果右括號比左括號多,不符合規則,回傳 False
if balance < 0: return False
# 遞迴出口,抵達終點,檢查左、右括號是否一樣多
if r == m-1 and c == n-1:
return balance == 0
# 遞迴,向右走
if r < m-1 and dfs(r+1, c, balance):
return True
# 遞迴,向下走
if c < n-1 and dfs(r, c+1, balance):
return True
# 預設回傳 False
return False
# 呼叫 dfs 求答案
return dfs(0, 0, 0)
C++ 程式碼
要使用 vector 當作 map 的 key,速度比較慢。Runtime: 50 ms, beats 86.60%. Memory: 39.75 MB, beats 46.61%.
class Solution {
private:
int m, n; // m 列、n 欄
map<vector<int>, bool> memo; // (r, c, balance): true or false
public:
// 記憶化 dfs,代入坐標 (r, c),左括號減右括號數量 balance
bool dfs(int r, int c, int balance, const vector<vector<char>>& grid) {
// 更新左括號減右括號數量
if (grid[r][c] == '(') balance++;
else balance--;
// 如果 memo 之中有已經算過的結果,直接回傳
vector<int> state = {r, c, balance};
if (memo.count(state) == 1) {
return memo[state];
}
// 剪枝,如果右括號比左括號多,不符合規則,回傳 False
if (balance < 0) return false;
// 遞迴出口,抵達終點,檢查左、右括號是否一樣多
if (r == m-1 && c == n-1) {
return balance == 0;
}
// 遞迴,向下走
if (r < m-1 && dfs(r+1, c, balance, grid)) {
memo[state] = true;
return true;
}
// 遞迴,向右走
if (c < n-1 && dfs(r, c+1, balance, grid)) {
memo[state] = true;
return true;
}
// 預設回傳 False
memo[state] = false;
return false;
}
bool hasValidPath(vector<vector<char>>& grid) {
m = (int)grid.size();
n = (int)grid[0].size();
// 特例,如果總步數 m + n - 1 是奇數,不可能平衡
if ((m + n - 1) % 2 == 1) return false;
// 特例,grid[0][0] 是 ),不可能平衡
if (grid[0][0] == ')') return false;
// 特例,grid[m-1][n-1] 是 (,不可能平衡
if (grid[m-1][n-1] == '(') return false;
// 呼叫 dfs 求答案
return dfs(0, 0, 0, grid);
}
};
沒有留言:
張貼留言