原題鏈接在這里:https://leetcode.com/problems/longest-valid-parentheses/
題目:
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.
題解:
和Largest Rectangle in Histogram都是往棧內存index.?
這道題是求最長的括號序列,比較容易想到用棧這個數據結構。基本思路就是維護一個棧,遇到左括號就進棧,遇到右括號則出棧,并且判斷當前合法序列是否為最 長序列。不過這道題看似思路簡單,但是有許多比較刁鉆的測試集。具體來說,主要問題就是遇到右括號時如何判斷當前的合法序列的長度。比較健壯的方式如下:
(1) 如果當前棧為空,則說明加上當前右括號沒有合法序列(有也是之前判斷過的);
(2) 否則彈出棧頂元素,如果彈出后棧為空,則說明當前括號匹配,我們會維護一個合法開始的起點start, 合法序列的長度即為當前元素的位置 i-start+1;?否則如果棧內仍有元素,那肯定是"(". 合法序列就會從這個"("下一位開始. 長度為當前棧頂元素的位置的下一位到當前元素的距離. i-(stk.peek()+1)+1. 因為棧頂元素后面的括號對肯定是合法的, 而 且左括號出過棧了。
Time Complexity: O(n). Space: O(n).
AC Java:
1 public class Solution { 2 public int longestValidParentheses(String s) { 3 if(s == null || s.length() == 0){ 4 return 0; 5 } 6 7 int res = 0; 8 int start = 0; 9 10 Stack<Integer> stk = new Stack<Integer>(); 11 for(int i = 0; i<s.length(); i++){ 12 if(s.charAt(i) == '('){ 13 stk.push(i); 14 }else{ 15 //遇到')',但是stack是空的,說明不合法,更新 start 到 i+1 16 if(stk.isEmpty()){ 17 start = i+1; 18 }else{ 19 //否則彈出棧頂 20 stk.pop(); 21 //剩下的stack為空,說明到start 到 i是合法括號對 22 if(stk.isEmpty()){ 23 res = Math.max(res, i-start+1); 24 }else{ 25 //身下的stack不為空,說明當前stack的棧頂 后面的括號對才是合法的 26 res = Math.max(res, i-stk.peek()); 27 } 28 } 29 } 30 } 31 return res; 32 } 33 }
可以利用DP. 求最長的valid parentheses. 需要儲存的歷史信息是到當前位最長valid parentheses 長度.
狀態轉移, 若當前是")".?看當前括號的前一個括號的匹配情況,例如在7之前以6結尾的的最佳匹配是3-6, 看3之前的括號和7是否匹配,不匹配則沒有變化; 而6之前以5結尾的最佳匹配是4-5, 此時3和6匹配, 則dp[i]+2.
此外,如果與當前括號匹配的左括號之前的括號的dp值也應該加進來,因為由于添加了當前的括號,那些括號也被連接起來了。例如3和6匹配后, 1和2也應該被加到以6結尾的最佳匹配中.
)? (? )? (? (? )? )? )
0 1 2 3 4 5 6 7
Time Complexity: O(s.length()).
Space: O(s.length()).
AC Java:
class Solution {public int longestValidParentheses(String s) {if(s == null || s.length() == 0){return 0;}int res = 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>=0 ? 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>=0 ? dp[i-dp[i-1]-2] : 0) + 2;}}res = Math.max(res, dp[i]);}return res;} }
?也可以節省空間.?
分別計數遇到的"(" 和")"的個數l 和 r. 先從左向右掃, l == r時跟新維護res. 當r大于l 時不合法重置為0.
在反過來從右向左掃一次.
為什么不能一次呢. e.g. ()((). 中間出現不合法, 若最后及時r比l小, 但取出r*2. 答案就是4. 正解應該是2.
Time Complexity: O(s.length()). Space: O(1).
AC Java:
1 class Solution { 2 public int longestValidParentheses(String s) { 3 if(s == null || s.length() == 0){ 4 return 0; 5 } 6 7 int res = 0; 8 int l = 0; 9 int r = 0; 10 for(int i = 0; i<s.length(); i++){ 11 if(s.charAt(i) == '('){ 12 l++; 13 }else{ 14 r++; 15 } 16 17 if(l == r){ 18 res = Math.max(res, r*2); 19 }else if(r > l){ 20 l = 0; 21 r = 0; 22 } 23 } 24 25 l = 0; 26 r = 0; 27 for(int i = s.length()-1; i>=0; i--){ 28 if(s.charAt(i) == '('){ 29 l++; 30 }else{ 31 r++; 32 } 33 34 if(l == r){ 35 res = Math.max(res, r*2); 36 }else if(l > r){ 37 l = 0; 38 r = 0; 39 } 40 } 41 return res; 42 } 43 }
?