Given a string containing just the characters ‘(’ and ‘)’, find the length of the longest valid (well-formed) parentheses substring.
Example 1:
Input: “(()”
Output: 2
Explanation: The longest valid parentheses substring is “()”
Example 2:
Input: “)()())”
Output: 4
Explanation: The longest valid parentheses substring is “()()”
解法1:
用栈解决
假设有字符串 “) ( ) ( ) ) ( ( ( ( ) ) ) (”
start代表有效括号开始的位置,初始化为-1
遍历字符串
第一个是 ) 栈为空,此时无有效的(与它匹配,故将start的值变为i = 0
第二个是 ( 将i值入栈,此时栈: 1
第三个是 ) 栈不为空,说明有有效的(与它匹配,将左括号退栈,此时栈为空,将匹配的左括号退栈后再次检查栈是否为空
为空 :说明此时没有多余的(阻断有效的括号,我们可以通过 i - start来计算长度,此时为2 - 0 = 2,更新最大长度2
第四个是 ( 将i值入栈,此时栈: 3
第五个是 ) 栈不为空,说明有有效的(与它匹配,将左括号退栈,此时栈为空,将匹配的左括号退栈后再次检查栈是否为空
为空 :说明此时没有多余的(阻断有效的括号,我们可以通过 i - start来计算长度,此时为4 - 0 = 2,更新最大长度4
第六个是 ) 栈为空,此时无有效的(与它匹配,说明有非法的)阻断有效的字符串,故将start的值变为i = 5
7、8、9都是(,入栈,此时栈:6,7, 8
第十个是 ) 栈不为空,说明有有效的(与它匹配,将左括号退栈,此时栈为空,将匹配的左括号退栈后再次检查栈是否为空
此时栈:6,7,栈不为空,此时有多余的(阻断有效括号,我们不能用i - start来计算长度,应该用 i - st.top(),即9 -7 = 2,更新最大长度4
第十一个是 ) 栈不为空,说明有有效的(与它匹配,将左括号退栈,此时栈为空,将匹配的左括号退栈后再次检查栈是否为空
此时栈:6,栈不为空,此时有多余的(阻断有效括号,我们不能用i - start来计算长度,应该用 i - st.top(),即10 -6 = 4,更新最大长度4
第十二个是 ( 将i值入栈,此时栈: 6,11
结束算法
c++:
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> st;
int maxLen = 0;
int start = -1;
int n = s.size();
for(int i = 0; i < n; i++){
if(s[i] == '('){
st.push(i);
} else {
if(st.empty()){
start = i;
} else{
st.pop();
if(st.empty()){
maxLen = max(maxLen, i - start);
} else {
maxLen = max(maxLen, i - st.top());
}
}
}
}
return maxLen;
}
};
java:
class Solution {
public int longestValidParentheses(String s) {
Stack<Integer> st = new Stack<>();
int maxLen = 0;
int start = -1;
int n = s.length();
for(int i = 0; i < n; i++){
if(s.charAt(i) == '('){
st.push(i);
} else {
if(st.empty()){
start = i;
} else{
st.pop();
if(st.empty()){
maxLen = Math.max(maxLen, i - start);
} else {
maxLen = Math.max(maxLen, i - st.peek());
}
}
}
}
return maxLen;
}
}
python:
class Solution(object):
def longestValidParentheses(self, s):
"""
:type s: str
:rtype: int
"""
st = []
maxLen = 0;
start = -1;
n = len(s)
for i in xrange(n):
if s[i] == '(':
st.append(i)
else:
if not st:
start = i
else:
st.pop()
if not st:
maxLen = max(maxLen, i - start)
else:
maxLen = max(maxLen, i - st[-1])
return maxLen
解法1的变体:
为了实现两种计算长度的统一, i - st.top()和i - start,我们将要更新start值时将start值压入栈中,这样统一用 i - st.top()就可以了
java:
public class Solution {
public int longestValidParentheses(String s) {
int maxLen = 0;
Stack<Integer> st = new Stack<>();
st.push(-1);
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
st.push(i);
} else {
st.pop();
if (st.empty()) {
st.push(i);
} else {
maxLen = Math.max(maxLen, i - st.peek());
}
}
}
return maxLen;
}
}
解法1的变体2:
假设有字符串“())()((())”
首先初始化栈[0]
第一个是(: 是左括号,追加0,此时栈[0,0]
第二个是 ): 是右括号,查看栈中数字的数量,大于1说明有可以匹配的(,取出栈中此时的括号数量,为0,更新括号数量此时栈[2],更新最大的括号的数量,2
第三个是 ):是右括号,查看栈中数字的数量,为1,说明没有可以匹配的(,说明有不合法的),将栈初始化[0]
第四个是 (:是左括号,追加0,此时栈[0,0]
第五个是 ): 是右括号,查看栈中数字的数量,大于1说明有可以匹配的(,取出栈中此时的括号数量,为0,更新括号数量此时栈[2],更新最大的括号的数量,2
6,7,8都是(,此时栈[2,0,0,0]
第九个是 ): 是右括号,查看栈中数字的数量,大于1说明有可以匹配的(,取出栈中此时的括号数量,为0,更新括号数量此时栈[2,0,2],更新最大的括号的数量,2
第十个是 ): 是右括号,查看栈中数字的数量,大于1说明有可以匹配的(,取出栈中此时的括号数量,为2,更新括号数量此时栈[2,4],更新最大的括号的数量,4
python:
class Solution(object):
def longestValidParentheses(self, s):
"""
:type s: str
:rtype: int
"""
st = [0]
maxLen = 0
for c in s:
if c == '(':
st.append(0)
else:
if len(st) > 1:
val = st[-1]
st.pop()
st[-1] += val + 2
maxLen = max(st[-1], maxLen)
else:
st = [0]
return maxLen
解法2:
DP:
dp[i]表示为在位置i结尾的的有效括号长度
由定义知所有(的位置dp值都为0,因为以(结尾的都不是有效括号
在程序中我们只需处理)的dp值
假设有字符串“())()(())”
从第二个开始
第二个是),i-1 的字符是(,说明匹配成功个数在dp[i-2]的基础上增加了2,dp数组[0,2,0,0,0,0,0,0,0],更新最大长度2
第三个是),i-1的字符不是(,这时候可能匹配的(可能在前边,已知dp[i-1]的值为x,说明位置i-1结尾的的有效括号长度为x,所以我们要越过x个位置去找匹配的(,具体来说就是位置i - dp[i-1] - 1,此时匹配的(不存在, dp数组[0,2,0,0,0,0,0,0,0],更新最大长度2
第四个是(,不做处理,dp数组[0,2,0,0,0,0,0,0,0]
第五个是),i-1 的字符是(,说明匹配成功个数在dp[i-2]的基础上增加了2,dp数组[0,2,0,2,0,0,0,0,0],更新最大长度2
第六个是(,不做处理,dp数组[0,2,0,2,0,0,0,0,0]
第七个是(,不做处理,dp数组[0,2,0,2,0,0,0,0,0]
第八个是),i-1 的字符是(,说明匹配成功个数在dp[i-2]的基础上增加了2,dp数组[0,2,0,2,0,0,0,2,0],更新最大长度2
第三个是),i-1的字符不是(,这时候可能匹配的(可能在前边,已知dp[i-1]的值为2,说明位置i-1结尾的的有效括号长度为2,所以我们要越过2个位置去找匹配的(,具体来说就是位置j = i - dp[i-1] - 1 = 8 - 2 - 1 = 5,此时对应的是可以匹配的(,dp[i] = dp[i-1] + 2,此时还要关注前面是否有可以接上的有效括号,即再加上dp[j - 1] ,如此时dp[4] = 2,说明前面有可以接上的2个有效括号, dp数组[0,2,0,0,0,0,0,2,6],更新最大长度6
注意点:
- dp[i] = (i - 2) >= 0 ? dp[i-2] + 2 : 2 最后要加的是2而不是0
- dp[i] = dp[i-1] + 2 + ((j - 1) >= 0 ? dp[j - 1] : 0) 注意我们要加的是最后三元表达式的值,所以三元表达式要加括号
不能写成 dp[i] = dp[i-1] + 2 + (j - 1) >= 0 ? dp[j - 1] : 0 这样会先加dp[i-1] + 2 + (j - 1)的值,然后看是否>=0,然后返回dp[j - 1]或者0
原因是加减乘除优先级要大于比较
c++:
class Solution {
public:
int longestValidParentheses(string s) {
int maxLen = 0;
int n = s.size();
vector<int> dp(n, 0);
for(int i = 1; i < n; i++){
if(s[i] == ')'){
if(s[i-1] == '('){
dp[i] = (i - 2) >= 0 ? dp[i-2] + 2 : 2;
} else {
int j = i - dp[i-1] - 1;
if(j >= 0 && s[j] == '('){
dp[i] = dp[i-1] + 2 + ((j - 1) >= 0 ? dp[j - 1] : 0);
}
}
maxLen = max(maxLen, dp[i]);
}
}
return maxLen;
}
};
java:
class Solution {
public int longestValidParentheses(String s) {
int maxLen = 0;
int n = s.length();
int[] dp = new int[n];
for(int i = 1; i < n; i++){
if(s.charAt(i) == ')'){
if(s.charAt(i-1) == '('){
dp[i] = (i - 2) >= 0 ? dp[i-2] + 2 : 2;
} else {
int j = i - dp[i-1] - 1;
if(j >= 0 && s.charAt(j) == '('){
dp[i] = dp[i-1] + 2 + ((j - 1) >= 0 ? dp[j - 1] : 0);
}
}
maxLen = Math.max(maxLen, dp[i]);
}
}
return maxLen;
}
}
python:
注意点:dp[i] = dp[i-1] + 2 + (dp[j - 1] if (j - 1) >= 0 else 0) 必须在后边加括号,否则它的意思为 if (j - 1) >= 0 dp[i] = dp[i-1] + 2 + (dp[j - 1] 否则dp[i] = 0
class Solution(object):
def longestValidParentheses(self, s):
"""
:type s: str
:rtype: int
"""
n = len(s)
dp = [0] * n
maxLen = 0;
for i in xrange(1, n):
if s[i] == ')':
if s[i-1] == '(':
dp[i] = dp[i-2] + 2 if (i - 2) >= 0 else 2
else:
j = i - dp[i-1] - 1
if j >= 0 and s[j] == '(':
dp[i] = dp[i-1] + 2 + (dp[j - 1] if (j - 1) >= 0 else 0)
maxLen = max(maxLen, dp[i])
return maxLen;
解法三:两遍扫描法
这种方法不需要额外的空间
设置两个变量left和right,分别代表(和)的数量
首先先正向扫描,从左到右,分别记录(和)的数量,如果left = right 说明是有效字符串,记录它的数量left * 2,更新最大数量
如果)的数量大于(的数量,说明出现非法括号,(和)的数量置0
仅仅正向扫描还是不够的
如“()((())”有效括号为4,正向扫描只能得到2
首先先正向扫描,从左到右,分别记录(和)的数量,如果left = right 说明是有效字符串,记录它的数量left * 2,更新最大数量
如果(的数量大于)的数量,说明出现非法括号,(和)的数量置0
如“()((())”有效括号为4,经过正向反向扫描得到正确的答案4
C++:
class Solution {
public:
int longestValidParentheses(string s) {
int maxLen = 0;
int n = s.size();
int left = 0,right = 0;
for(int i = 0; i < n; i++){
if(s[i] == '('){
left++;
} else {
right++;
}
if(left == right){
maxLen = max(maxLen, 2 * right);
} else if(right > left){
left = right = 0;
}
}
left = right = 0;
for(int i = n-1; i >= 0; i--){
if(s[i] == '('){
left++;
} else {
right++;
}
if(left == right){
maxLen = max(maxLen, 2 * right);
} else if(left > right){
left = right = 0;
}
}
return maxLen;
}
};
java:
class Solution {
public int longestValidParentheses(String s) {
int maxLen = 0;
int n = s.length();
int left = 0,right = 0;
for(int i = 0; i < n; i++){
if(s.charAt(i) == '('){
left++;
} else {
right++;
}
if(left == right){
maxLen = Math.max(maxLen, 2 * right);
} else if(right > left){
left = right = 0;
}
}
left = right = 0;
for(int i = n-1; i >= 0; i--){
if(s.charAt(i) == '('){
left++;
} else {
right++;
}
if(left == right){
maxLen = Math.max(maxLen, 2 * right);
} else if(left > right){
left = right = 0;
}
}
return maxLen;
}
}
python:
class Solution(object):
def longestValidParentheses(self, s):
"""
:type s: str
:rtype: int
"""
maxLen, n , left , right = 0, len(s), 0, 0
for c in s:
if c == '(':
left += 1
else:
right += 1
if left == right:
maxLen = max(maxLen, 2 * right)
elif right > left:
left = right = 0
left = right = 0;
for c in s[::-1]:
if c == '(':
left += 1
else:
right += 1
if left == right:
maxLen = max(maxLen, 2 * right)
elif left > right:
left = right = 0
return maxLen;