1. 程式人生 > 實用技巧 >[LeetCode] 32 Longest Valid Parentheses (棧 or DP)

[LeetCode] 32 Longest Valid Parentheses (棧 or DP)

題意:

給你一個括號序列,求其中最長的合法括號序列的長度。

思路:

這個題的核心思路,其實是合法括號序列的定義。

合法括號的定義如下:

  1. "()" 是一個合法括號序列;
  2. 如果 "|" 表示一個合法序列,那麼 "(|)" 也是一個合法序列;
  3. 如果 "|" 表示一個合法序列,那麼 "||" 也是一個合法序列。

一個簡單思路是,列舉所有序列,然後判斷這個序列是否是合法序列。判斷方法是:對於一個序列,可以根據定義遞迴判斷是否符合上面三條之一。如果用記憶化搜尋,最多需要計算 O(N^2) 次判斷過程,然後列舉也是 O(N^2) 次。所以這種思路的整體時間複雜度是 O(N^2)。但是這個題 O(N^2) 會 TLE。

根據括號匹配棧的特性,可以維護每一段合法括號的長度,最後找最大的一段合法括號序列。

根據定義,我們可以按下面方式來求出一個合法括號序列:

  1. 定義一個棧,來維護括號序列;
  2. 如果遇到 "(" 則直接入棧;
  3. 如果遇到 ")":
    1. 如果棧頂是 "(",則 "(" 出棧,入棧 "()" 的合法序列;
    2. 如果棧頂兩個元素形如 "(" + "|",則出棧這兩個元素,入棧 "(|)";
    3. 合併棧頂的連續合法序列
class Solution:
    def longestValidParentheses(self, s: str) -> int:
        if not s:
            return 0

        stack = []
        for c in s:
            if '(' == c:
                stack.append(1)
            else:
                top = 0
                while stack and stack[-1] % 2 == 0:
                    top += stack.pop()
                if stack and stack[-1] == 1:
                    stack.pop()
                    top += 2
                    stack.append(top)
                else:
                    if top:
                        stack.append(top)
                    stack.append(-1)
        rlt = 0
        cur = 0
        for i in stack:
            if i % 2 == 1:
                cur = 0
            else:
                cur += i
            rlt = max(rlt, cur)
        return rlt

DP

dp 思路如下: 用 dp[i] 表示結尾為第 i 字元的最長合法序列的長度。根據定義可以得到以下遞推關係:

  1. 若第 i 個字元是 "(",則 dp[i] = 0;
  2. 若第 i 個字元是 ")",則:
    1. 若第 i-1 個字元是 "(" 則 dp[i] 為 "()" 的長度加它前面一段的最長合法長度(定義3);
    2. 若 dp[i-1] > 0 (前面是一個合法序列),則許要看這個序列前一個字元是不是 "("。(定義2)
class Solution:
    def longestValidParentheses(self, s: str) -> int:
        if not s: return 0
        
        dp = [0 for _ in s]
        for idx, c in enumerate(s):
            if ')' == c:
                if idx > 0 and s[idx-1] == '(':
                    dp[idx] = (dp[idx-2] if idx > 1 else 0) + 2
                    continue
                if (dp[idx-1] > 0) and (idx > dp[idx-1]) and (s[idx-dp[idx-1]-1] == '('):
                    dp[idx] = dp[idx-1] + 2
                    if idx-dp[idx-1]-1 > 0:
                        dp[idx] += dp[idx-dp[idx-1]-2]
        return max(dp)