Title
給定一個只包含 ‘(’ 和 ‘)’ 的字符串,找出最長的包含有效括號的子串的長度。
示例 1:
輸入: “(()”
輸出: 2
解釋: 最長有效括號子串爲 “()”
示例 2:
輸入: “)()())”
輸出: 4
解釋: 最長有效括號子串爲 “()()”
棧
Solve
括號匹配問題,第一個想到的數據結構就是棧,考慮到邊界情況,要保持棧底元素爲當前已經遍歷過的元素中最後一個沒有被匹配的右括號的下標,初始情況放入-1,棧裏其他元素維護左括號的下標。
具體過程:
- 如果遇到左括號,將其下標放入棧中
- 如果遇到右括號:
- 如果棧爲空,說明當前的右括號爲沒有被匹配的右括號,我們將其下標放入棧中來更新我們之前提到的「最後一個沒有被匹配的右括號的下標」
- 如果棧不爲空,當前右括號的下標減去棧頂元素即爲「以該右括號爲結尾的最長有效括號的長度」
Code
def longestValidParentheses_stack(self, s: str) -> int:
stack, ans = [-1], 0
for i in range(len(s)):
if s[i] == '(':
stack.append(i)
else:
stack.pop()
if not stack:
stack.append(i)
else:
ans = max(ans, i - stack[-1])
return ans
複雜度分析
時間複雜度: O(n),n 是給定字符串的長度。我們只需要遍歷字符串一次即可。
空間複雜度: O(n)。棧的大小在最壞情況下會達到 n,因此空間複雜度爲 O(n) 。
動態規劃
Solve
定義dp數組並全部初始化爲0,dp[i]表示以下標i字符結尾的最長有效括號的長度。
有效子串一定以右括號結尾,所有以左括號結尾的子串對應的dp值必定爲0。
從前往後遍歷字符串求解dp值,每兩個字符檢查一次:
- s[i]=’)’,s[i-1]=’(’,可以推出:dp[i]=dp[i−2]+2
- s[i]=’)’,s[i-1]=’)’,如果s[i−dp[i−1]−1]=’(’,可以推出:dp[i]=dp[i−1]+dp[i−dp[i−1]−2]+2
我們考慮如果倒數第二個 ‘)’ 是一個有效子字符串的一部分(記作 subs),對於最後一個 ‘)’ ,如果它是一個更長子字符串的一部分,那麼它一定有一個對應的 ‘(’ ,且它的位置在倒數第二個 ‘)’ 所在的有效子字符串的前面(也就是 subs 的前面)。
因此,如果子字符串 subs 的前面恰好是 ‘(’ ,那麼我們就用 2 加上 subs 的長度(dp[i−1])去更新 dp[i]。同時,我們也會把有效子串 “(subs)” 之前的有效子串的長度也加上,也就是再加上 dp[i−dp[i−1]−2]。
最後的答案即爲 dp 數組中的最大值。
Code
def longestValidParentheses_dp(self, s: str) -> int:
length = len(s)
if length == 0:
return 0
dp = [0] * length
for i in range(length):
if s[i] == ')' and i - dp[i - 1] - 1 >= 0 and s[i - dp[i - 1] - 1] == '(':
dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2
return max(dp)
複雜度分析
時間複雜度: O(n),其中 n 爲字符串的長度。我們只需遍歷整個字符串一次,即可將 dp 數組求出來。
空間複雜度: O(n)。我們需要一個大小爲 n 的 dp 數組。