1. 题目描述
最长连续有效括号,给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
- 示例 1:
输入: “(()”
输出: 2
解释: 最长有效括号子串为 “()” - 示例 2:
输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”
2. 代码实现
2.1 动态规划
执行用时 :48 ms, 在所有 Python 提交中击败了50.82%的用户
内存消耗 :13.3 MB, 在所有 Python 提交中击败了16.67%的用户
关于动态规划的介绍和解读,我在算法:动态规划(详解及例题)中有详细介绍。下面为具体算法分析步骤:
我们定义一个 dp 数组,其中第 i 个元素表示以下标为 i 的字符结尾的最长有效子字符串的长度。将 dp 数组全部初始化为 0 。现在,很明显有效的子字符串一定以 ‘)’ 结尾。
这进一步可以得出结论:以‘(’ 结尾的子字符串对应的 dp 数组位置上的值必定为 0。所以说我们只需要更新 ‘)’ 在 dp 数组中对应位置的值。
为了求出 dp 数组,我们每两个字符检查一次,如果满足如下条件:
s[i]=‘)’ 且 s[i−1]=‘(’
也就是字符串形如 “……()”,我们可以推出:
dp[i]=dp[i−2]+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
即如果倒数第二个 ‘)’ 是一个有效子字符串的一部分(记为 sub_s),对于最后一个 ‘)’ ,如果它是一个更长子字符串的一部分,那么它一定有一个对应的 ‘(’ ,它的位置在倒数第二个 ‘)’ 所在的有效子字符串的前面(也就是sub_s的前面)。因此,如果子字符串 sub_s的前面恰好是 ‘(’ ,那么我们就用 2 加上 sub_s的长度(dp[i−1])去更新dp[i]。除此以外,我们也会把有效子字符串 "(,sub_s,)"之前的有效子字符串的长度也加上,也就是加上dp[i−dp[i−1]−2]
class Solution(object):
def longestValidParentheses(self, s):
"""
:type s: str
:rtype: int
"""
lens = len(s)
dp = [0 for _ in range (lens+1)]
for i in range(2,lens):
if s[i] == ")" and s[i- dp[i-1] - 1]=="(":
dp[i] = dp[i-1]+dp[i-dp[i-1]-2] + 2
return dp[-1]
2.2 栈实现
执行用时 :36 ms, 在所有 Python 提交中击败了90.68%的用户
内存消耗 :12.9 MB, 在所有 Python 提交中击败了16.67%的用户
与找到每个可能的子字符串后再判断它的有效性不同,我们可以用栈在遍历给定字符串的过程中去判断到目前为止扫描的子字符串的有效性,同时能的都最长有效字符串的长度。我们首先将 −1 放入栈顶。
对于遇到的每个(’ ,我们将它的下标放入栈中。
对于遇到的每个‘)’ ,我们弹出栈顶的元素并将当前元素的下标与弹出元素下标作差,得出当前有效括号字符串的长度。通过这种方法,我们继续计算有效子字符串的长度,并最终返回最长有效子字符串的长度。
class Solution:
def longestValidParentheses(self, s):
"""
:type s: str
:rtype: int
"""
stack = [0]
longest = 0
for c in s:
if c == "(":
stack.append(0)
else:
if len(stack) > 1:
val = stack.pop()
stack[-1] += val + 2
longest = max(longest, stack[-1])
else:
stack = [0]
return longest