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 数组。