第51题 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.

Hide Tags
  Dynamic Programming String






Solution in Java using DP 1:
<span style="font-size:18px;">public class Solution {
    public int longestValidParentheses(String s) {
        if(s==null||s.length()<2) return 0;
        
        int[] opj  = new int[s.length()]; 
        //opj[i] denotes the maximum length of the valid substring from 0 to i, <span style="color:#ff0000;"><strong>containing s[i]!!!</strong></span>
        //so we have:
        opj[0] = 0;
        int max = 0;
        
        for(int i=1; i<s.length(); i++){
            if(s.charAt(i)==')'){
                int leftIndex = i - opj[i-1] -1;
                //leftIndex is the index of the matching '(' if it exists.
                if(leftIndex>=0&&s.charAt(leftIndex)=='('){
                    opj[i] = opj[i-1] + 2;
                    if(leftIndex>=2)    opj[i]+=opj[leftIndex-1];
                }
            }
            max = Math.max(opj[i], max);
            
        }
        return max;
        
        
    }
}</span>

Note:注意opj[i]记录s[0...i]中最长的合法子字符串,但该字符串一定包含s[i]!就是说这个子串在s[0...i]中一定是靠右的!!而leftIndex则为该字串的左边界,所以opj[i]记录的最长子串为:s[leftIndex...i],并且s[leftIndex...i]必然形成了一个闭合的括号,即有"(...)"的形式。

Solution in Java using DP 2 (从右往左的DP):
public class Solution {
    public int longestValidParentheses(String s) {
        if(s==null||s.length()<2) return 0;
        int len = s.length();
        int[] opj  = new int[len]; 
        //opj[i] denotes the maximum length of the valid substring from i to len-1, 
        //containing s[i]!!! So we have:
        opj[len-1] = 0;
        int max = 0;
        
        for(int i=len-2; i>=0; i--){
            if(s.charAt(i)=='('){
                int rightIndex = i+ opj[i+1] + 1;
                //rightIndex is the index of the matching ')' if it exists.
                if(rightIndex<len&&s.charAt(rightIndex)==')'){
                    opj[i] = opj[i+1] + 2;
                    if(rightIndex<len-2)    opj[i]+=opj[rightIndex+1];
                }
            }
            max = Math.max(opj[i], max);
            
        }
        return max;
        
        
    }
}

Note: 从右往左算,则对opj[i],其记录的最长有效子串是在s[i...len-1]中靠左的子串,该子串必须包含s[i]。rightIndex为该子串的右边界。
Solution in Java using DP 3 (DP with a little modification):
public class Solution {
    public int longestValidParentheses(String s) {
        if(s==null||s.length()<2) return 0;
        int len = s.length();
        int[] opj  = new int[len]; 
        //opj[i] denotes the maximum length of the valid substring from i to len-1, 
        //containing s[i]!!! So we have:
        opj[len-1] = 0;
        int max = 0, valid=0;   
        //using valid to record if there is a matching ')' for a '('
        //for each ')', valid++;  for each '(', if valid>0 then this '(' is valid, and valid--;
        
        for(int i=len-1; i>=0; i--){
            if(s.charAt(i)=='('&&valid>0){
                opj[i]  = 2 + opj[i+1] + (i+opj[i+1]+1<len-2?opj[i+opj[i+1]+1+1]:0);
                valid--;
            }
            else if(s.charAt(i)==')')   valid++;
            max = Math.max(opj[i], max);
            
        }
        return max;
        
        
    }
}

Solution in Java using a stack and a boolean array:
public class Solution {
    public int longestValidParentheses(String s) {
        if(s==null||s.length()<2) return 0;
        boolean[] valid = new boolean[s.length()];
        Stack<Integer> indices = new Stack<Integer>();
        
        for(int i=0; i<s.length(); i++){
            if(s.charAt(i)=='(')    indices.push(i);
            if(s.charAt(i)==')'&&!indices.empty()){
                valid[i] = true;
                valid[indices.pop()]=true;
            }
        }
        
        int max=0, len = 0;
        for(int i=0; i<s.length();i++){
            if(valid[i])    len++;
            if(!valid[i]||i==s.length()-1)   {
                max = Math.max(max, len);
                len=0;
            }
        }
        return max;
        
        
    }
}

Note: 
很多人会说这道题用动规,可是用动规每次匹配后还要向前到上一个匹配跟这个匹配是否连接,时间复杂度为O(n^2),其实可以换个想法,用一个bool数组来标记已经匹配过的字符,找到最长的连续标记的长度就是所求的结果。只要遍历两遍数组,时间复杂度为O(n)。(last note is referenced from: http://www.cnblogs.com/easonliu/p/3637429.html)

最长有效括号子串问是经典的算法问,目标是找出一个只包含 `'('` 和 `')'` 的字符串中最长的连续有效括号子串的长度。该问可以通过多种方法解决,其中使用动态规划(Dynamic Programming)和栈(Stack)是最常见的两种方式。 ### 动态规划方法 动态规划方法通过构建一个 `dp` 数组来记录以每个字符结尾的最长有效子串长度。具体步骤如下: - 初始化一个长度为 `n` 的数组 `dp`,初始值为 0。 - 遍历字符串,当遇到 `')'` 时进行判断: - 如果前一个字符是 `'('`,则判断是否形成 `()` 形式的有效括号,若成立,则更新当前 `dp[i]`。 - 如果前一个字符是 `')'`,则检查是否存在嵌套的有效括号结构,并更新当前 `dp[i]`。 ```c #include <stdio.h> #include <string.h> int longestValidParentheses(char* s) { int n = strlen(s); int dp[n]; memset(dp, 0, sizeof(dp)); int maxLen = 0; for (int i = 1; i < n; i++) { if (s[i] == ')') { if (s[i - 1] == '(') { dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; } else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(') { dp[i] = dp[i - 1] + ((i - dp[i - 1] >= 2) ? dp[i - dp[i - 1] - 2] : 0) + 2; } maxLen = (dp[i] > maxLen) ? dp[i] : maxLen; } } return maxLen; } ``` ### 栈方法 栈方法通过维护一个栈来记录括号的索引,用于判断括号是否匹配并计算有效长度。具体步骤如下: - 初始化一个栈,初始时压入 `-1` 作为基准值。 - 遍历字符串,遇到 `'('` 时压入栈。 - 遇到 `')'` 时弹出栈顶元素,如果栈为空,则压入当前索引;否则计算当前索引与栈顶元素的差值,更新最大长度。 ```c #include <stdio.h> #include <string.h> #define MAX(a, b) ((a) > (b) ? (a) : (b)) int longestValidParentheses(char* s) { int n = strlen(s); int stack[n + 1]; int top = 0; stack[0] = -1; int maxLen = 0; for (int i = 0; i < n; i++) { if (s[i] == '(') { stack[++top] = i; } else { top--; if (top == -1) { stack[++top] = i; } else { maxLen = MAX(maxLen, i - stack[top]); } } } return maxLen; } ``` ### 复杂度分析 - **时间复杂度**:两种方法均为 $O(n)$,其中 $n$ 是字符串的长度。 - **空间复杂度**: - 动态规划方法为 $O(n)$,需要额外的 `dp` 数组。 - 栈方法为 $O(n)$,需要额外的栈空间。 ### 应用场景 - **动态规划**:适用于对内存使用要求不高的场景,且需要快速实现。 - **栈方法**:适用于内存敏感的场景,且需要高效处理嵌套结构的问
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值