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。
下面是代码,是不是很复杂很想给博客君一巴掌。
解法一:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
plus,这是O(n3)的。。。。“该吃药了。。亲”。如期望的,该解法在大集合的时候超时。
解法二:来自leetcode讨论组的写法。本来人有O(n)的解法,被楼主活生生地卖弄成了O(n3)。楼主真想挖个坑把自己埋了!!!
大家首先看,这个解法里面的stack,不是用来存左右括号的。人是来存左括号的index。本来么,右括号也不用存。遍历S。遇到'(',放入lefts。如果遇到')',如果lefts是空,说明这是一个无法匹配的')',记录下last。last里面存放的其实是最后一个无法匹配的')'。为啥要保存这个值呢?主要是为了计算后面完整的表达式的长度。可以这样理解: “所有无法匹配的')'”的index其实都是各个group的分界点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
总结下:
DP不是万能的。注意发现问题的本质。不过这个确实要靠足够的练习。解法二的代码确实很简洁,但是并不是人人都能想到的。