Longest Valid Parentheses 最长合法括号
一、问题描述
给一个只由’(‘和’)'组成的字符串,找出其中最长的连续合法子串长度。(来源Leetcode)
样例
Input:(()
Output:2
Input: )()())
Output: 4
Input: ((())
Output: 4
二、解决方案
很明显,可以用动态规划,我也是这么想的。
1. 我的方案
构建dp[len][len], dp[i][j]表示以i开头,j结尾的子串中最长子串。s表示字符串,然后给出递推式
注意,循环的时候注意
class Solution {
public:
int longestValidParentheses(string s) {
int len = s.size();
vector<vector<int> > dp(len, vector<int>(len, 0));
int max = 0;
for(int i = len - 1; i >= 0; -- i){
for(int j = i + 1; j < len; j += 2){
if(i + 1 == j && s[i] == '(' && s[j] == ')')
dp[i][j] = 2;
else if(dp[i+1][j-1] > 0 && s[i] == '(' && s[j] == ')')
dp[i][j] = dp[i+1][j-1] + 2;
else{
for(int k = i + 1; k < j; k += 2){
if(dp[i][k] > 0 && dp[k+1][j] > 0){
dp[i][j] = dp[k+1][j] + dp[i][k];
break;
}
}
}
if(max < dp[i][j])
max = dp[i][j];
}
}
return max;
}
};
结果分析,虽说用了动态规划的思想,但是明显时间和空间复杂度过高,尽管有一定技巧性,但本质上是 , 还不如暴力搜索,至少暴力搜索的空间复杂度是 。
2. Leetcode上的解决方案
原文链接,以下内容主要是来源于LeetCode上的解决方案,我主要是翻译了一下,主要分为暴力,动态规划,堆栈,无需额外空间法。
2.1暴力(Brute Force)
找出所有偶数子串,然后使用堆栈判断是否是合法括号。
时间复杂度和空间复杂度为
public class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<Character>();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
stack.push('(');
} else if (!stack.empty() && stack.peek() == '(') {
stack.pop();
} else {
return false;
}
}
return stack.empty();
}
public int longestValidParentheses(String s) {
int maxlen = 0;
for (int i = 0; i < s.length(); i++) {
for (int j = i + 2; j <= s.length(); j+=2) {
if (isValid(s.substring(i, j))) {
maxlen = Math.max(maxlen, j - i);
}
}
}
return maxlen;
}
}
2.2 动态规划(Dynamic Programming)
只需一个一维动态数组,dp[i]表示,以第i个字符结尾的合法子串长度。换句话说,合法子串包括第i个字符。时间复杂度和空间复杂度分别为
。下面给出递推式
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;
}
}
2.3 栈(Using Stack)
新建一个Stack,
- 压入 -1;
- 对每个字符,如果是’(’,压入该索引,
- 如果是’)’, 弹出栈顶元素,此时用该索引,减去弹出元素之后的栈顶元素,即为当前合法子串长度。如果栈为空,则将’)'的索引压入栈中。
该算法的时间复杂度和空间复杂度为
public class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
Stack<Integer> stack = new Stack<>();
stack.push(-1);
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
stack.push(i);
} else {
stack.pop();
if (stack.empty()) {
stack.push(i);
} else {
maxans = Math.max(maxans, i - stack.peek());
}
}
}
return maxans;
}
}
2.4 无需额外空间(Without extra space)
用两个变量left和right存储左右括号数量,当left == right,表示当前最大子串长度,当right > left,表示遇到不合法,两个变量置0。当遇到"(()",此时该方法失效,因此从左往右扫,然后从右往左扫,即可完美解决所有情况。
该算法的时间复杂度和空间复杂度为
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;
}
}