LeetCode 32. 最長有效括號 | Python

32. 最長有效括號


題目來源:力扣(LeetCode)https://leetcode-cn.com/problems/longest-valid-parenthesespython

題目


給定一個只包含 '(' 和 ')' 的字符串,找出最長的包含有效括號的子串的長度。bash

示例 1:微信

輸入: "(()"
輸出: 2
解釋: 最長有效括號子串爲 "()"

示例 2:app

輸入: ")()())"
輸出: 4
解釋: 最長有效括號子串爲 "()()"

解題思路


思路:棧優化

題目中,要查找有效括號,有可能須要從內往外擴展,這符合棧先入後出的特性。code

咱們先使用暴力解,來嘗試解決這個問題。blog

有效的括號是成對出現的,那麼咱們嘗試羅列全部可能的非空偶數長度子字符串,判斷其是否有效。索引

當咱們遇到左括號 ( 時,咱們將它入棧。當遇到右括號 ) 時,從棧中彈出一個左括號 (,若是棧中沒有了左括號,或者遍歷完成後棧中還保留有元素,那麼這個子字符串就是無效的。循環遍歷,更新最大的長度。具體的代碼以下:leetcode

def longestValidParentheses(self, s: str) -> int:
    def is_valid(s):
        stack = []
        for i in range(len(s)):
            if s[i] == '(':
                stack.append('(')
            elif stack and stack[-1] == '(':
                stack.pop()
            else:
                return False
        return not stack

    max_len = 0
    for i in range(len(s)):
        for j in range(i+2, len(s)+1, 2):
            if is_valid(s[i:j]):
                max_len = max(max_len, j-i)

    return max_len

可是這裏最終的執行結果是超時。由於咱們的方法是從字符串中羅列出全部可能的非空偶數子字符串,這裏須要的時間複雜度爲 O(n^2)字符串

在這裏,咱們嘗試進行優化。

上面的暴力解法,是要先羅列全部可能的子字符串。在這裏,咱們直接在遍歷的過程當中去判斷所遍歷的子字符串的有效性,同時維護最大的長度。

這裏有個須要注意的地方,開始的時候,須要先將 -1 壓入棧中(後面解釋)。具體實現的方法以下:

  • 當遇到 ( 時,將其索引壓入棧中;
  • 當遇到 ) 時,彈出棧頂元素,計算有效長度,循環遍歷,維護最大的長度。

第二步中,計算有效長度。這裏直接用當前遍歷的字符索引減去棧頂元素的索引。此時的棧頂索引對應的是:有效子字符串的前一位字符的索引值,用示例 1 來解釋下:

"(()"

按照前面的方法,實現的過程以下(stack 表示棧,i 爲遍歷時的索引值):

  • 先將 -1 壓入棧中,那麼 stack = [-1]
  • 開始遍歷,先遇到 (,索引壓入棧中,stack = [-1, 0], i = 0
  • 繼續遍歷,仍是 (,索引入棧,stack=[-1, 0, 1], i = 1
  • 最後,遇到的是 ),將棧頂元素出棧,stack=[-1, 0], i = 2,這個時候,計算最大有效長度:當前字符索引減去棧頂元素的索引,也就是 len = 2-0。此時這個棧頂元素 0,就是最開始的 ( 第一個左括號對應的索引。

至於爲何要先將 -1 壓入棧中,這裏舉例解釋下:

"()"

若是遇到的是這樣的字符串,那麼按照上面的實現過程,當遍歷結束後,棧是空。那麼計算最大有效長度便會出錯。若是事先將 -1 壓入棧中,那麼此時棧中的元素爲 stack=[-1],那麼這裏能夠計算出結果。

再舉一個例子:

")()"

在這個字符串中,若是先遇到的是 ),那麼須要先進行出棧操做,這裏若是棧中沒有元素,這裏一樣會報錯。

具體的實現代碼以下。

代碼實現


class Solution:
    def longestValidParentheses(self, s: str) -> int:
        max_len = 0
        stack = []
        # 當先遇到 `)` 須要先彈出元素,這裏能夠防止報錯
        # 當遇到的 `()` 的字符時,-1 在計算長度的時候,發揮做用
        stack.append(-1)
        for i in range(len(s)):
            if s[i] == '(':
                stack.append(i)
            else:
                stack.pop()
                if not stack:
                    stack.append(i)
                else:
                    max_len = max(max_len, i - stack[-1])
        return max_len

實現結果


實現效果

總結


  • 由於要找出有效的括號,有可能會遇到括號由內往外擴展,這跟棧先入後出的特性類似,能夠考慮使用棧的方法來解決;
  • 首先使用暴力解的方法嘗試解決問題,在這裏,羅列出全部非空偶數子字符串,檢查他的有效性,統計最大的有效長度(可是執行以後發現超時)
  • 這裏對暴力解進行優化,不使用羅列的方法。直接在遍歷字符串的時候,檢查遍歷掃描的子字符串是否有效同時計算有效長度,具體的作法以下:
    • 先將 -1 壓入棧中(具體的緣由前面已分析)
    • 當遇到 ( 時,將其索引壓入棧中,
    • 當遇到 ) 時,出棧,計算有效長度(用當前遍歷的字符索引減去此時棧頂元素)。
    • 循環遍歷,獲得最大的有效長度。

文章原創,若是以爲寫得還能夠,歡迎關注點贊。微信公衆號《書所集錄》同步更新,一樣歡迎關注點贊。

相關文章
相關標籤/搜索