32. Longest Valid Parentheses
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 “()()”
solution 1 : sliding windows
维护两个ptr, l , r, 从0开始,递增l直到遇到第一个 " ( " "(" "(",然后开始从这个 l 开始滑动 r,直到open和close个数相等,或者close个数超过open个数,终止当前循环,寻找下一个 l 。
public int longestValidParentheses(String s) {
if (s.length() <= 1) return 0;
int l = 0;
int r = 0;
int open = 0;
int res = 0;
while (l < s.length()) {
int close = 0;
while (l < s.length() && s.charAt(l) != '(')
l++;
if (l >= s.length()) break;
r = l + 1;
open = 1;
while (r < s.length()) {
if (s.charAt(r) == '(')
open++;
else
close++;
if (close > open) break;
if (open == close)
res = Math.max(res, r-l+1);
r++;
}
l++;
}
return res;
}
solution 2 : DP
This problem can be solved by using Dynamic Programming. We make use of a dp \text{dp} dp array where iith element of dp \text{dp} dp represents the length of the longest valid substring ending at iith index. We initialize the complete %\text{dp}% array with 0’s. Now, it’s obvious that the valid substrings must end with ‘)’ \text{‘)’} ‘)’. This further leads to the conclusion that the substrings ending with ‘(’ \text{‘(’} ‘(’ will always contain ‘0’ at their corresponding dp \text{dp} dp indices. Thus, we update the dp \text{dp} dp array only when ‘)’ \text{‘)’} ‘)’ is encountered.
To fill dp \text{dp} dp array we will check every two consecutive characters of the string and if
s [ i ] = ‘)’ \text{s}[i] =\text{‘)’} s[i]=‘)’ and s [ i − 1 ] = ‘(’ \text{s}[i - 1] = \text{‘(’} s[i−1]=‘(’, i.e. string looks like . . . . . . . ( ) " ⇒ .......()" \Rightarrow .......()"⇒ dp [ i ] = dp [ i − 2 ] + 2 \text{dp}[i]=\text{dp}[i-2]+2 dp[i]=dp[i−2]+2
We do so because the ending “()” portion is a valid substring anyhow and leads to an increment of 2 in the length of the just previous valid substring’s length.
s [ i ] = ‘)’ \text{s}[i] = \text{‘)’} s[i]=‘)’ and s [ i − 1 ] = ‘)’ \text{s}[i - 1] = \text{‘)’} s[i−1]=‘)’, i.e. string looks like . . . . . . . ) ) " ⇒ .......))" \Rightarrow .......))"⇒ if s [ i − dp [ i − 1 ] − 1 ] = ‘(’ \text{s}[i - \text{dp}[i - 1] - 1] = \text{‘(’} s[i−dp[i−1]−1]=‘(’ then dp [ i ] = dp [ i − 1 ] + dp [ i − dp [ i − 1 ] − 2 ] + 2 \text{dp}[i]=\text{dp}[i-1]+\text{dp}[i-\text{dp}[i-1]-2]+2 dp[i]=dp[i−1]+dp[i−dp[i−1]−2]+2
The reason behind this is that if the 2nd last ‘)’ \text{‘)’} ‘)’ was a part of a valid substring (say s u b s sub_s subs), for the last ‘)’ \text{‘)’} ‘)’ to be a part of a larger substring, there must be a corresponding starting ‘(’ \text{‘(’} ‘(’ which lies before the valid substring of which the 2nd last ‘)’ \text{‘)’} ‘)’ is a part (i.e. before s u b s sub_s subs). Thus, if the character before s u b s sub_s subs happens to be ‘(’ \text{‘(’} ‘(’, we update the dp [ i ] \text{dp}[i] dp[i] as an addition of 2 2 2 in the length of s u b s sub_s subs which is dp [ i − 1 ] \text{dp}[i-1] dp[i−1]. To this, we also add the length of the valid substring just before the term “(,sub_s,)” , i.e. dp [ i − dp [ i − 1 ] − 2 ] \text{dp}[i-\text{dp}[i-1]-2] dp[i−dp[i−1]−2].
public class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
int dp[] = new int[s.length()];
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i) == ')') {
if (s.charAt(i - 1) == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
maxans = Math.max(maxans, dp[i]);
}
}
return maxans;
}
}
solution 3 :
In this approach, we make use of two counters leftleft and rightright. First, we start traversing the string from the left towards the right and for every encountered, we increment the leftleft counter and for every ‘)’ \text{‘)’} ‘)’ encountered, we increment the rightright counter. Whenever leftleft becomes equal to rightright, we calculate the length of the current valid string and keep track of maximum length substring found so far. If rightright becomes greater than leftleft we reset leftleft and rightright to 00.
Next, we start traversing the string from right to left and similar procedure is applied.
public class Solution {
public int longestValidParentheses(String s) {
int left = 0, right = 0, maxlength = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = Math.max(maxlength, 2 * right);
} else if (right >= left) {
left = right = 0;
}
}
left = right = 0;
for (int i = s.length() - 1; i >= 0; i--) {
if (s.charAt(i) == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = Math.max(maxlength, 2 * left);
} else if (left >= right) {
left = right = 0;
}
}
return maxlength;
}
}
There are only three cases for a string:
- ‘(’ are more than ‘)’
- ‘(’ are less than ‘)’
- ‘(’ and ‘)’ are the same
Now, when you scan from left to right, you can only find the correct maxLength for cases 2 and 3, because if it is case 1, where ‘(’ \text{‘(’} ‘(’ are more than ‘)’ \text{‘)’} ‘)’ (e.g., "((()" \text{"((()"} "((()"), then left is always greater than right and maxLength does not have the chance to be updated.
Similarly, when you scan from right to left, you can only find the maxLength for cases 1 and 3.
Therefore, a two-pass scan covers all the cases and is guaranteed to find the correct maxLength