【刷題LeetCode】奇妙動態規劃——最長有效括號

動態規劃的重點推導出狀態轉移方程

題目描述

先來看下這個題目描述:

給定一個只包含 ‘(’ 和 ‘)’ 的字符串,找出最長的包含有效括號的子串的長度。
示例
輸入: “)()())”
輸出: 4
解釋: 最長有效括號子串爲 “()()”

參閱官方題解中「方法2-動態規劃」

題解-初步理解

  • 初始化dp數組,dp[0]*len(s)
  • dp[i]表示以該位元素結尾的最長有效子字符串的長度
  • 在遍歷字符串的時候,只要該位是左括號,就保持dp[i] = 0
  • 從後向前推導dp之間的狀態轉移方程,假設我們現在位於i == 5
    在這裏插入圖片描述
  • 更新i = 4的位置爲2
  • 接下來要判斷位是有效子括號對前面那位,即i = 2,發現這也是一對有效子括號
    在這裏插入圖片描述
  • 那麼與5對應的左括號一定不在這個有效對中咯,繼續向前尋找,發現dp[0]=0,所以最終有效括號即爲這兩對dp[2]+dp[4]=4

這個示例沒有找到前面所對應的與最後一位匹配的左括號,但是可以幫助我們先理解狀態數組,以及如何尋找與i對應的位置,即減去內部的有效子串dp[i-1],我們再看一個示例:

題解-更進一步

“()((())”

  • 初始化這個dp數組,先填入不「鑲嵌」的有效子括號對。

在這裏插入圖片描述

  • 現在我們來觀察i = 3,已知基礎長度爲2,更新dp[6]爲2
  • 在上一狀態中,我們知道i = 3和i =6中還夾了一對有效子串,並存儲在i = 5中,因此
    dp[i] = 2 + dp[i-1],因此dp[5]更新爲4.
    在這裏插入圖片描述
    在這裏插入圖片描述
  • i = 2時爲0,因此i = 6保持不變,依然爲4。

題解-最終推導

現在來看當查找外部時爲右括號的情況

“()(())”

在這裏插入圖片描述

  • 狀態轉移方程變成
    dp[i] = 2 + dp[i-1] + dp[i-dp[i-1]-2]
  • 我們用這個方程求解一下dp[1]
    dp[1] = 2 + dp[0] + dp[1-dp[0]-2] = 2 + 0 + dp[-1]
    dp[-1]不存在,因此爲0,最後dp[1] = 2
  • 你以爲結束了麼?No,這裏存在一個問題,就是在python中dp[-2]是倒數第2個元素,本例中dp[-2] = 2
  • 所以我們要加入一個限制條件,即被查找元素的值一定要大於等於0

代碼實現

class Solution2:
    """動態規劃 i-dp[i-1]-1是與當前)對稱的位置"""
    def longestValidParentheses(self, s: str) -> int:
        n = len(s)
        if n == 0:
            return 0
        dp = [0]*n
        for i in range(len(s)):
            if s[i] == ')' and i-dp[i-1]-1 >= 0 and s[i-dp[i-1]-1]=='(':
                dp[i] = 2 + dp[i-1] + dp[i-dp[i-1]-2] 
        return max(dp)

另一種神奇而被人喜歡的方法

雖然動態規劃很厲害,但我個人還是很喜歡方法4…

class Solution4:
    """正向逆向相結合的方法
    當右括號個數 > 左括號個數的時候,直接將"左括號個數"、"右括號個數"重新初始化爲0
    逆向遍歷時,當右括號 < 左括號個數,直接將"左括號個數"、"右括號個數"重新初始化爲0
    時間複雜度O(N),空間複雜度O(1)
    """
    def longestValidParentheses(self, s: str) -> int:
        n, left, right, maxlength = len(s), 0, 0, 0
        # 正序遍歷
        for i in range(n):
            if s[i] == '(':
                left += 1
            else:
                right += 1
            if left == right:
                maxlength = max(maxlength,2*right)
            elif right > left:
                left = right = 0
        # 反序遍歷
        left = right = 0
        for i in range(n-1,-1,-1):
            if s[i] == '(':
                left += 1
            else:
                right += 1
            if left == right:
                maxlength = max(maxlength,2*left)
            elif right < left:
                left = right = 0
        return maxlength

感覺就是思考能簡單則簡單哈哈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章