• 32. 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.

    链接: http://leetcode.com/problems/longest-valid-parentheses/

    题解:

    一开始想要尝试跟Valid Parentheses类似的解法,就是维护一个leftCount,一个rightCount,当leftCount < rightCount的时候清零,当leftCount = rightCount时,尝试更新max,结果出错。后来看到曹神的解法,才发现还应该从字符串尾部向前再来一遍。

    下面是解法,two passes,  Time Complexity - O(n), Space Complexity - O(1)。

    public class Solution {
        public int longestValidParentheses(String s) {          //if left parentheses count > right parentheses count, case always valid
            if(s == null || s.length() == 0)
                return 0;
            int start = -1, depth = 0, max = 0;
            
            for(int i = 0; i < s.length(); i++) {
                if(s.charAt(i) == '(')
                    depth++;
                else {
                    depth--;
                    if(depth < 0) {
                        start = i;
                        depth = 0;
                    }
                    
                    if(depth == 0) 
                        max = Math.max(max, i - start);
                }
            }
            
            depth = 0;
            start = s.length();
            
            for(int i = s.length() - 1; i >= 0; i--) {      //if right parentheses count > left parentheses count, case always valid
                if(s.charAt(i) == ')') 
                    depth++;
                else {
                    depth--;
                    if(depth < 0) {
                        start = i;
                        depth = 0;
                    }
                    
                    if(depth == 0)
                        max = Math.max(max, start - i);
                }
            }
            
            return max;
        }
    }

    还有一种解法是one pass, 方法是维护一个stack以及一个start index,遇到左括号入栈,遇到右括号出栈,当stack为空时说明左右括号平衡,计算i 与 start的距离, 否则左括号数目>右括号数目,计算i 与当前栈顶元素距离。 有小trick可以设置start = -1,这样代码里计算距离就可以不用 + 1了。还可以稍微简化一下,不过逻辑下面更清楚。

    Time Complexity - O(n), Space Complexity - O(n)。

    public class Solution {
        public int longestValidParentheses(String s) {
            if(s == null || s.length() == 0)
                return 0;
            Stack<Integer> stack = new Stack<>();
            int start = 0, max = 0;
            
            for(int i = 0; i < s.length(); i++) {
                if(s.charAt(i) == '(')          //record index of each '(' 
                    stack.push(i);
                else {
                    if(stack.isEmpty())         //try to find first '('
                        start = i + 1;
                    else {
                        stack.pop();
                        if(stack.isEmpty())     //left num = right num
                            max = Math.max(max, i - start + 1);
                        else                    //left > right,  cal max with current top element in stack
                            max = Math.max(max, i - stack.peek());
                    }
                }
            }
            
            return max;
        }
    }

    二刷:

    可以用三种方法来做:

    1. 一种是曹神的方法,使用类似valid parentheses的方法。
      1. 维护一个depth,一个count。
      2. 从左向右遍历时并且当char == '('时,depth++,否则char = ')',depth--,这时候我们count++,因为找到了一对valid parenthese
      3. 当depth == 0的时候,左右括号平衡,可以尝试更新max, max = Math.max(max, count * 2)
      4. 接下来判断depth是否小于0,小于0的话depth = 0, count = 0,我们从头开始计算。
      5. 左右各自遍历一遍。从右向左遍历是为了计算类似于"()(()()"这种情况,这时depth always > 0,没办法得到max = 4的结论。
    2. 一种是一维DP,分好几种情况,画一个decision tree会比较清楚逻辑。
      1. 维护一个数组max[], 其中max[i]代表以s.charAt(i)结尾的longest valid parentheses的长度。我们考虑接下来集中情况。
      2. max[0] = 0,因为此时不能组成"()"。所以我们可以直接从 i = 1开始遍历
      3. 当前字符是'(', max[i] = 0,因为valid parentheses不能以'('结尾
      4. 否则,当前字符等于')',这时候继续判断几种情况
        1. s.charAt(i - 1) = '(',正好可以组成一对括号。
          1. 当 i - 2 >= 0,max[i] = max[i - 2] + 2
          2. 当 i - 2 < 0, max[i] = 2
        2. 否则s.charAt(i - 1) = ')',此时我们也是继续进行判断
          1. 此时我们要求出i关于max[i - 1]对称的字符,就是 i - max[i - 1] - 1
            1. 假如i - max[i - 1] - 1 >= 0,并且 s.charAt(i - max[i - 1] - 1) == '('
              1. 此时表示从i - max[i - 1] - 1到i这一段都合理,所以这一部分等于max[i - 1] + 2, 我们要继续判断  i - max[i - 1] - 2
                1. 当i - max[i - 1] - 2 >= 0, 则 max[i] = max[i - 1] + 2 + max[i - max[i - 1] - 2]
                2. 否则max[i] = max[i - 1] + 2
            2. 否则max[i] = 0,我们不改变什么 
      5. 在开头维护一个res = 0, 每次计算完max[i]之后尝试更新这个res,最后返回的也是这个res.
      6. 其实还可以继续简化,留给三刷了
    3. 一种是利用一个stack来计算,这个留给三刷了,也是O(n)和O(n)

    Java:

    Time Complexity - O(n), Space Complexity - O(1)

    public class Solution {
        public int longestValidParentheses(String s) {
            if (s == null || s.length() == 0) {
                return 0;
            }
            int count = 0, max = 0, depth = 0;
            for (int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                if (c == '(') {
                    depth++;
                } else {
                    depth--;
                    count++;
                    if (depth == 0) {
                        max = Math.max(max, count * 2);    
                    }
                    if (depth < 0) {
                        depth = 0;
                        count = 0;
                    }
                }
            }
            depth = 0;
            count = 0;
            for (int i = s.length() - 1; i >= 0; i--) {
                char c = s.charAt(i);
                if (c == ')') {
                    depth++;
                } else {
                    depth--;
                    count++;
                    if (depth == 0) {
                        max = Math.max(max, count * 2);    
                    }
                    if (depth < 0) {
                        depth = 0;
                        count = 0;
                    }
                }
            }
            return max;
        }
    }

    DP - Time Complexity - O(n), Space Complexity - O(n)

    public class Solution {
        public int longestValidParentheses(String s) {
            if (s == null || s.length() == 0) {
                return 0;
            }
            int[] max = new int[s.length()];     // max[i] contains longest valid parentheses end at i
            int res = 0;
            for (int i = 1; i < max.length; i++) {
                char c = s.charAt(i);
                if (c == '(') {
                    max[i] = 0;
                } else {                // c = ')'
                    if (s.charAt(i - 1) == '(') {
                        max[i] = i - 2 >= 0 ? max[i - 2] + 2 : 2;
                    } else {
                        if (i - max[i - 1] - 1 >= 0 && s.charAt(i - max[i - 1] - 1) == '(') {
                            max[i] = max[i - 1] + 2 + (i - max[i - 1] - 2 >= 0 ? max[i - max[i - 1] - 2] : 0);
                        }
                    }
                }
                res = Math.max(max[i], res);
            }
            return res;
        }
    }

    题外话:

    1-20-2016 

    DP一直学得不好, divide and conquer也学得不好,需要多练习多思考。 像Matrix multiply, counting inversion,closest pair,merge sort之类的,一定要多多练习。还有Weighted quick union with path compression, run-length coding, Huffman Tree等等。

    三刷:

    还是使用曹神的方法,赞曹神思路清晰。

    Java:

    public class Solution {
        public int longestValidParentheses(String s) {
            if (s == null || s.length() == 0) return 0;
            int max = 0, count = 0, start = 0, len = s.length();
            for (int i = 0; i < len; i++) {
                if (s.charAt(i) == '(') count++;
                else count--;
                if (count == 0) max = Math.max(max, i - start + 1);
                if (count < 0) {
                    count = 0;
                    start = i + 1;
                }
            }
            start = len - 1;
            count = 0;
            for (int i = len - 1; i >= 0; i--) {
                if (s.charAt(i) == ')') count++;
                else count--;
                if (count == 0) max = Math.max(max, start - i + 1);
                if (count < 0) {
                    count = 0;
                    start = i - 1;
                }
            }
            return max;
        }
    }

    Reference:

    http://weibo.com/cpcs     曹神微博

    http://www.cnblogs.com/springfor/p/3869495.html   小莹子

    https://leetcode.com/discuss/9156/my-solution-using-one-stack-in-one-pass

    https://leetcode.com/discuss/21549/simple-java-solution-o-n-time-one-stack

    https://leetcode.com/discuss/8092/my-dp-o-n-solution-without-using-stack

    https://leetcode.com/discuss/7609/my-o-n-solution-using-a-stack

  • 相关阅读:
    Dephi XE 编译后执行文件的路径怎么改
    一名Delphi程序员的开发习惯
    Delphi AnimateWindow 用法 淡入淡出窗口
    Delphi开发DLL
    delphi 中配置文件的使用(*.ini)
    Delphi中根据分类数据生成树形结构的最优方法
    Delphi語法筆記
    2015年10月19日 做过的面试题(四)
    ios 客户端定位的3种方法
    常用开源镜像站整理android sdk manager
  • 原文地址:https://www.cnblogs.com/yrbbest/p/4435218.html
Copyright © 2020-2023  润新知