LeetCode 2267. Check if There Is a Valid Parentheses String Path
周賽292。終於久違的又四題AK了,只是這次網站有點問題,搞不好不會計分,好難受。
題目
一個括號字串是一個非空且只包含’(‘和’)’的字串。如果滿足下列任一條件,則這個括號字串就是合法的:
- 字串是()
- 字串可以表示為AB,代表A連接B,且A和B都是合法括號字串
- 字串可以表示為(A),且A是合法括號字串
輸入M*N的括號矩陣grid。一個合法的括號路徑必須滿足以下條件:
- 開始於最左上角(0,0)
- 結束於最右下角(M-1,N-1)
- 每次只會往右或是往下方移動
- 路徑經過的格子所組成的括號字串是合法的
如果grid中存在任何合法括號路徑,回傳true,否則回傳false。
解法
老實說第一段的合法括號解釋有點臭長,我直接跳過不管,看圖例就懂了。
簡單講是要從左上角出發,走到右下角,途中經過的括號字串要能夠成功對上。
一開始沒注意到只能往右或往下,想說這樣好像很難判斷哪些格子能不能走,浪費一些時間。
重新看過題目,恍然大悟,不會出現回頭路就很簡單了,只要維護左括號的數量,直接dfs走到底就好。
dfs寫好交出去,這垃圾網站伺服器掛掉,提交的時候只給我TLE,但是沒給測資,想說該不會dfs不行,就改用bfs去。
結果還是TLE,仔細想想,可能是不同的路線抵達同樣位置,且擁有同樣的括號數量,造成多次重複計算,最後加set去重複就過了。
重點在於檢查路上的括號字串是否合法:左括號一定要先出現,才能配上右括號。
我們維護變數left代表左括號的數量,每碰到左括號則+1,右括號則-1,如果left值哪天小於0,代表不合法,拋棄此路徑。
最後抵達右下角時,還要再檢查一次是否每個左括號都有被關起來,此時left應為0。
class Solution:
def hasValidPath(self, grid: List[List[str]]) -> bool:
M, N = len(grid), len(grid[0])
visited=set()
q = deque([(0, 0, 0)])
while q:
r, c, left = q.popleft()
if not (0 <= r < M and 0 <= c < N):
continue
if grid[r][c] == '(':
left += 1
else:
left -= 1
if left < 0:
continue
if r == M-1 and c == N-1 and left == 0:
return True
if (r,c,left) not in visited:
visited.add((r, c, left))
q.append((r+1, c, left))
q.append((r, c+1, left))
return False
再寫一次dfs,2008ms,可能因為少走很多冤枉路,比上面的6781ms快了不少。
class Solution:
def hasValidPath(self, grid: List[List[str]]) -> bool:
M, N = len(grid), len(grid[0])
visited=set()
def dfs(r,c,left):
if not (r<M and c<N) or (r,c,left) in visited:
return False
visited.add((r,c,left))
if grid[r][c]=='(':
left+=1
else:
left-=1
if left<0:
return False
if r==M-1 and c==N-1 and left==0:
return True
return dfs(r+1,c,left) or dfs(r,c+1,left)
return dfs(0,0,0)
2022-5-11更新DP解法加上剪枝。總感覺這難度比dfs高出好多,執行時間也有點抖抖。
因為只能往右和下走,總路徑長度一定是M+N-1。如果路徑長度是奇數,一定不可能湊成對的括號,直接回傳false;左上角非’(‘或右下角非’)’也可以直接判斷。
先計算出路徑數path,對每個位置開出path長度的陣列,表示以多少左括號到達該點。
既然起點一定是左括號,就將dp[0][0][1]初始化為1,由上到下,由左到右做DP。
遍歷所有格子(r,c),並更新所有可能的括號數量0到(r+c+1)。最後在右下角必須要所有括號都成對,左括號必須為0,回傳dp[M-1][N-1][0]。
class Solution:
def hasValidPath(self, grid: List[List[str]]) -> bool:
M,N=len(grid),len(grid[0])
path=(M+N-1)
if path&1 or grid[0][0]==')' or grid[-1][-1]=='(':
return False
dp=[[[False]*(path+5) for _ in range(N)] for _ in range(M)]
dp[0][0][1]=True
for r in range(M):
for c in range(N):
diff=1 if grid[r][c]=='(' else -1
for i in range(min(path,r+c+1)):
if (i>0 or diff!=-1):
if r>0:
dp[r][c][i+diff]|=dp[r-1][c][i]
if c>0:
dp[r][c][i+diff]|=dp[r][c-1][i]
return dp[-1][-1][0]