最长合法括号

Longest Valid Parentheses 最长合法括号

一、问题描述

给一个只由’(‘和’)'组成的字符串,找出其中最长的连续合法子串长度。(来源Leetcode)

样例

Input:(()
Output:2

Input: )()())
Output: 4

Input: ((())
Output: 4

二、解决方案

很明显,可以用动态规划,我也是这么想的。

1. 我的方案

构建dp[len][len], dp[i][j]表示以i开头,j结尾的子串中最长子串。s表示字符串,然后给出递推式
d p [ i ] [ j ] = { 2 , if i+1 = j and s[i] = ’(’ and s[j] = ’)’ d p [ i + 1 ] [ j − 1 ] + 2 , if dp[i+1][j-1] > 0 and s[i] = ’(’ and s[j] = ’)’ d p [ k + 1 ] [ j ] + d p [ i ] [ k ] , if dp[i][k] > 0 and dp[k+1][j] > 0,  k ∈ ( i , j ) dp[i][j] = \begin {cases} 2, & \text{if i+1 = j and s[i] = '(' and s[j] = ')'}\\ dp[i+1][j-1] + 2, & \text{if dp[i+1][j-1] > 0 and s[i] = '(' and s[j] = ')'}\\ dp[k+1][j] + dp[i][k], & \text{if dp[i][k] > 0 and dp[k+1][j] > 0, } k \in (i, j)\\ \end {cases} dp[i][j]=2,dp[i+1][j1]+2,dp[k+1][j]+dp[i][k],if i+1 = j and s[i] = ’(’ and s[j] = ’)’if dp[i+1][j-1] > 0 and s[i] = ’(’ and s[j] = ’)’if dp[i][k] > 0 and dp[k+1][j] > 0, k(i,j)
注意,循环的时候注意 i ∈ ( l e n , 0 ] , j ∈ [ i + 1 , l e n ) , k ∈ ( i , j ) i \in (len, 0], j \in[i+1, len), k \in (i, j) i(len,0],j[i+1,len),k(i,j)

class Solution {
public:
    int longestValidParentheses(string s) {
        int len = s.size();
        vector<vector<int> > dp(len, vector<int>(len, 0));
        int max = 0;
        for(int i = len - 1; i >= 0; -- i){
            for(int j = i + 1; j < len; j += 2){
                if(i + 1 == j && s[i] == '(' && s[j] == ')')
                    dp[i][j] = 2;
                else if(dp[i+1][j-1] > 0 && s[i] == '(' && s[j] == ')')
                    dp[i][j] = dp[i+1][j-1] + 2;
                else{
                    for(int k = i + 1; k < j; k += 2){
                        if(dp[i][k] > 0 && dp[k+1][j] > 0){
                            dp[i][j] = dp[k+1][j] + dp[i][k];
                            break;
                        }
                    }
                }
                if(max < dp[i][j])
                    max = dp[i][j];
            }
        }
        return max;
    }
};

结果分析,虽说用了动态规划的思想,但是明显时间和空间复杂度过高,尽管有一定技巧性,但本质上是 O ( n 3 ) , O ( n 2 ) O(n^3), O(n^2) O(n3),O(n2), 还不如暴力搜索,至少暴力搜索的空间复杂度是 O ( n ) O(n) O(n)

2. Leetcode上的解决方案

原文链接,以下内容主要是来源于LeetCode上的解决方案,我主要是翻译了一下,主要分为暴力,动态规划,堆栈,无需额外空间法。

2.1暴力(Brute Force)

找出所有偶数子串,然后使用堆栈判断是否是合法括号。
时间复杂度和空间复杂度为 O ( n 3 ) , O ( n ) O(n^3), O(n) O(n3),O(n)

public class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<Character>();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                stack.push('(');
            } else if (!stack.empty() && stack.peek() == '(') {
                stack.pop();
            } else {
                return false;
            }
        }
        return stack.empty();
    }
    public int longestValidParentheses(String s) {
        int maxlen = 0;
        for (int i = 0; i < s.length(); i++) {
            for (int j = i + 2; j <= s.length(); j+=2) {
                if (isValid(s.substring(i, j))) {
                    maxlen = Math.max(maxlen, j - i);
                }
            }
        }
        return maxlen;
    }
}

2.2 动态规划(Dynamic Programming)

只需一个一维动态数组,dp[i]表示,以第i个字符结尾的合法子串长度。换句话说,合法子串包括第i个字符。时间复杂度和空间复杂度分别为 O ( n ) , O ( n ) O(n), O(n) O(n),O(n)。下面给出递推式
d p [ i ] = { d p [ i − 2 ] + 2 , if i is even and s[i-1] = ’(’ and s[i] = ’)’ d p [ i − 1 ] + d p [ i − d p [ i − 1 ] − 2 ] + 2 , if i is even and s[i-1] = ’)’ and s[i] = ’)’ and s[i−dp[i−1]−1]=’(’ 0 , others dp[i] = \begin {cases} dp[i-2] + 2, &amp; \text{if i is even and s[i-1] = &#x27;(&#x27; and s[i] = &#x27;)&#x27;}\\ dp[i-1] + dp[i - dp[i-1] - 2] + 2, &amp; \text{if i is even and s[i-1] = &#x27;)&#x27; and s[i] = &#x27;)&#x27; and s[i−dp[i−1]−1]=&#x27;(&#x27;}\\ 0, &amp; \text{others}\\ \end {cases} dp[i]=dp[i2]+2,dp[i1]+dp[idp[i1]2]+2,0,if i is even and s[i-1] = ’(’ and s[i] = ’)’if i is even and s[i-1] = ’)’ and s[i] = ’)’ and s[i−dp[i−1]−1]=’(’others

public class Solution {
    public int longestValidParentheses(String s) {
        int maxans = 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 ? 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 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                }
                maxans = Math.max(maxans, dp[i]);
            }
        }
        return maxans;
    }
}

2.3 栈(Using Stack)

新建一个Stack,

  1. 压入 -1;
  2. 对每个字符,如果是’(’,压入该索引,
  3. 如果是’)’, 弹出栈顶元素,此时用该索引,减去弹出元素之后的栈顶元素,即为当前合法子串长度。如果栈为空,则将’)'的索引压入栈中。
    该算法的时间复杂度和空间复杂度为 O ( n ) , O ( n ) O(n), O(n) O(n),O(n)
public class Solution {

    public int longestValidParentheses(String s) {
        int maxans = 0;
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                stack.push(i);
            } else {
                stack.pop();
                if (stack.empty()) {
                    stack.push(i);
                } else {
                    maxans = Math.max(maxans, i - stack.peek());
                }
            }
        }
        return maxans;
    }
}

2.4 无需额外空间(Without extra space)

用两个变量left和right存储左右括号数量,当left == right,表示当前最大子串长度,当right > left,表示遇到不合法,两个变量置0。当遇到"(()",此时该方法失效,因此从左往右扫,然后从右往左扫,即可完美解决所有情况。
该算法的时间复杂度和空间复杂度为 O ( n ) , O ( 1 ) O(n), O(1) O(n),O(1)

public class Solution {
    public int longestValidParentheses(String s) {
        int left = 0, right = 0, maxlength = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxlength = Math.max(maxlength, 2 * right);
            } else if (right >= left) {
                left = right = 0;
            }
        }
        left = right = 0;
        for (int i = s.length() - 1; i >= 0; i--) {
            if (s.charAt(i) == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxlength = Math.max(maxlength, 2 * left);
            } else if (left >= right) {
                left = right = 0;
            }
        }
        return maxlength;
    }
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值