Question:
Given a string containing just the characters ‘(’ and ‘)’, find the length of the longest valid (well-formed) parentheses substring.

For “(()”, the longest valid parentheses substring is “()”, which has length = 2.

Another example is “)()())”, where the longest valid parentheses substring is “()()”, which has length = 4.


本题难度hard。有两种算法分别是:stack和DP。

算法1:stack

【复杂度】
时间 O(N) 空间 O(N)

【思路】
用Stack的方法本质上和Valid Parentheses是一样的,一个右括号能消去Stack顶上的一个左括号。不同的是,为了能够计算括号对的长度我们还需要记录括号们的下标。这样在弹出一个左括号后,我们可以根据当前坐标减去栈中上一个(也就是Pop过后的Top元素)的坐标来得到该有效括号对的长度。

【附】
这个算法难在对各种情况的判断,以及对stack的巧妙利用。这个算法我是看了很一会才反应过来((⊙﹏⊙)b)。下面将代码贴出,对于各种情况的处理我也进行了注释。

【注意】
第26行:​​​curLen=i-stack.peek().index;​​​不能是​​curLen=i-stack上一个pop出的index+1;​​​。如果对于​​)()()​​​,此时 i=4,如果按照后一种方法,算出的​​curLen=i-3+1​​​,而正确的应该是​​curLen=i-0​​。

【代码】

public class Solution {
public int longestValidParentheses(String s) {
//require
if(s==null)
return 0;
int size=s.length();
if(size<2)
return 0;
int ans=0;
Stack<Parenthese> stack=new Stack<Parenthese>();
//invariant
for(int i=0;i<size;i++){
char c=s.charAt(i);
//遇到左括号,将其push进栈
if(c=='(')
stack.push(new Parenthese(c,i));
else{
//遇到右括号,分类讨论
//如果当前栈顶是左括号,则消去并计算长度
if(!stack.isEmpty()&&stack.peek().c=='('){
int curLen=0;
stack.pop();
if(stack.isEmpty()) // ()( )
curLen=i+1;
else // ((() ) or )()( )
curLen=i-stack.peek().index;
ans=Math.max(ans,curLen);
}else // ) or ) )
//如果栈顶是右括号或者是空栈,则将右括号也push进栈,它的坐标将方便之后计算长度
stack.push(new Parenthese(c,i));
}

}
//ensure
return ans;
}

class Parenthese{
char c;
int index;
public Parenthese(char c,int index){
this.c=c;
this.index=index;
}
}
}

算法2:DP

【复杂度】
时间 O(N) 空间 O(N)

【思路】
动态规划法将大问题化为小问题,我们不一定要一下子计算出整个字符串中最长括号对,我们可以先从后向前,一点一点计算。假设d[i]是从下标i开始到字符串结尾最长括号对长度,s[i]是字符串下标为i的括号。如果s[i-1]是左括号,如果i + d[i] + 1是右括号的话,那d[i-1] = d[i] + 1。如果不是则为0。如果s[i-1]是右括号,因为不可能有右括号开头的括号对,所以d[i-1] = 0。

【附】
我只能再次感慨DP的强大和这个算法的精妙。

【注意】
1、第16行​​​if(end<size&&s.charAt(end)==')'){​​​中的​​end<size​​​必须要有,以防止​​(()​​​(当i=0,end=3越界)
2、第19行​​​d[i]+=d[end+1];​​​,别错写为​​d[i]+=d[end];​

【代码】

public class Solution {
public int longestValidParentheses(String s) {
//require
if(s==null)
return 0;
int size=s.length();
if(size<2)
return 0;
int ans=0;
int[] d=new int[size];
//invariant
for(int i=size-2;i>=0;i--){
char c=s.charAt(i);
if(c=='('){
int end=i+d[i+1]+1;
if(end<size&&s.charAt(end)==')'){ // (())
d[i]=d[i+1]+2;
if(end+1<size) // ()() or ((())())
d[i]+=d[end+1];
}
}
ans=Math.max(ans,d[i]);
}
//ensure
return ans;
}

}

参考

​​Longest Valid Parentheses​​(这篇帖子无论是技术探索精神还是写作风格都值得参考)