LeetCode 筆記系列八 Longest Valid Parentheses [lich你又想多了]


題目: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.

拿到題目,哎呀,這不是典型的動態規划嘛,然后刷刷開始coding。這道題確實可以用動態規划,但是復雜度就上去了,事實證明也沒法通過大集合的測試。不過還是可以當一個DP的練習。如果你比較忙,直接看解法二吧。

用longestValid[i][j]表示從S串中的字符i到j的最長well-formed表達式的長度;isValid[i][j]表示從i到j是否是一個valid的表達式。

如何判斷longestValid[i][j]的取值呢?有下面幾種情況:

假定有一個maxlength變量。

1. 如果i='(' 並且 j=')',

  a. 如果j = i+1,那這是一個valid的表達式;

      b. 如果isValid[i+1][j-1]為真,那這也是一個valid表達式;

  c. 對所有在i到j之間的k,如果isValid[i,k]&&isValid[k+1,j]為真,那么這是一個valid表達式,這三種情況maxlength都是i到j的距離;

  d.其他情況,maxlength = longestValid[i][j-1]和 longestValid[i+1][j]比較大的那個。

2. 如果i='(' 並且 j='(', maxlength等於longestValid[i][j-1]。

3. 如果i=')' 並且 j=')', maxlength等於longestValid[i+1][j]。

4. 如果i=')‘ 並且 j='(', maxlength等於longestValid[i-1][j-1].

判斷完成后,longestValid[i][j]賦值於maxlength。

下面是代碼,是不是很復雜很想給博客君一巴掌。

解法一:

 1 private static int longestValidParentheses(String s) {
 2         // Start typing your Java solution below
 3         // DO NOT write main() function
 4         if(s.length() == 0) return 0;
 5         boolean[][] isValid = new boolean[s.length()][];//isValid[i][j] is true when from i to j, this is a valid parentheses expression
 6         int[][] longestValid = new int[s.length()][];//the longest length of valid expression between i and j
 7         for(int i = 0; i < s.length();i++){
 8             isValid[i] = new boolean[s.length()];
 9             longestValid[i] = new int[s.length()];
10         }
11         for(int j = 1; j < s.length();j++){
12             for(int i = j - 1; i >=0;i--){
13                 int left = s.charAt(i);
14                 int right = s.charAt(j);
15                 int maxLength = 0;
16                 if(left == '(' && right ==')'){
17                     if(i + 1 == j) {
18                         isValid[i][j] = true;
19                         if(maxLength < 2) maxLength = 2;
20                     }else {
21                         if(isValid[i+1][j-1]){
22                             isValid[i][j] = true;
23                             if(maxLength < j - i + 1) maxLength = j - i + 1;
24                         }else if(isValid[i][i+1] && isValid[j-1][j] && 
25                                 (isValid[i + 2][j - 2] ||  j - i == 3)){
26                             isValid[i][j]=true;
27                             if(maxLength < j - i + 1) maxLength = j - i + 1;
28                         }else {
29                             maxLength = Math.max(longestValid[i][j-1], longestValid[i+1][j]);
30                             for(int k = i + 1; k < j;k++){
31                                 if(isValid[i][k] && isValid[k + 1][j]) {
32                                     maxLength = j - i + 1;
33                                     isValid[i][j]=true;
34                                     break;
35                                 }
36                             }
37                         }
38                     }
39                 }else {
40                     if(left == '(' && right == '(' 
41                             && longestValid[i][j-1] > maxLength) maxLength = longestValid[i][j-1];
42                     if(left == ')' && right == ')' 
43                             && longestValid[i+1][j] > maxLength)maxLength = longestValid[i+1][j];
44                     if(left ==')' && right == '(' 
45                             && longestValid[i+1][j-1] > maxLength){
46                         maxLength = longestValid[i+1][j-1];
47                     }
48                 }
49                 longestValid[i][j] = maxLength;
50             }
51         }
52         return longestValid[0][s.length()-1];
53     }
View Code

plus,這是O(n3)的。。。。“該吃葯了。。親”。如期望的,該解法在大集合的時候超時。

解法二:來自leetcode討論組的寫法。本來人有O(n)的解法,被樓主活生生地賣弄成了O(n3)。樓主真想挖個坑把自己埋了!!!

大家首先看,這個解法里面的stack,不是用來存左右括號的。人是來存左括號的index。本來么,右括號也不用存。遍歷S。遇到'(',放入lefts。如果遇到')',如果lefts是空,說明這是一個無法匹配的')',記錄下last。last里面存放的其實是最后一個無法匹配的')'。為啥要保存這個值呢?主要是為了計算后面完整的表達式的長度。可以這樣理解: “所有無法匹配的')'”的index其實都是各個group的分界點。

 1 public static int longestValidParentheses2(String s) {
 2         int maxLen = 0, last = -1;
 3         Stack<Integer> lefts = new Stack<Integer>();
 4         for (int i=0; i<s.length(); ++i) {
 5             if (s.charAt(i)=='(') {
 6                 lefts.push(i);
 7             } else {
 8                 if (lefts.isEmpty()) {
 9                     // no matching left
10                     last = i;
11                 } else {
12                     // find a matching pair
13                     lefts.pop();
14                     if (lefts.isEmpty()) {//有一個完整的valid的group。計算該group的長度
15                         maxLen = Math.max(maxLen, i-last);
16                     } else {
17                         //棧內還有‘(',一個最外層完整的group還沒有匹配完成,
18                         //但是通過查詢下一個即將匹配還未匹配的"("的index來更新maxLen。
19                         maxLen = Math.max(maxLen, i-lefts.peek());
20                     }
21                 }
22             }
23         }
24         return maxLen;
25     }
View Code

總結下:

DP不是萬能的。注意發現問題的本質。不過這個確實要靠足夠的練習。解法二的代碼確實很簡潔,但是並不是人人都能想到的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM