最長合法括號
阿新 • • 發佈:2018-11-26
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;
}
}