32.最长有效括号
给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

示例 1:
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"

示例 2:
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"

示例 3:
输入:s = ""
输出:0

题目分析

显然,该问题满足最优子结构特性,因此可以采用动态规划方法求解。令\(dp[i]\)表示以下标为\(i\)结尾的字符串(之所以这样设计状态,是为了满足无后效性),初始时\(dp\)数组全部赋值为0。
接下来考虑状态转移方程:由题意可知,若字符串以"("结尾,那必然是格式不正确的字符串,故长度为0,因此只需要考虑以")"结尾的字符串。
如果\(s[i]=')' \&\& s[i - 1]='('\),即字符串形如\(...()\),那么\(dp[i]=dp[i-2]+2\)
如果\(s[i]=')' \&\& s[i - 1]=')'\),即字符串形如\(...))\),那么需要再进行一步判断,即如果\(s[i-dp[i-1]-1]='('\),那么这就是一个合法的字符串。但是进行状态转移时需要注意到直接连接在该字符串之前的合法字符串,也要将其的长度给加上,即\(dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2]\)
代码如下:

class Solution {
public:
    int longestValidParentheses(string s) {
        int ans = 0;
        int n = s.size();
        vector<int> dp(n, 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] - 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;
                }
                ans = max(ans, dp[i]);
            }
        }
        return ans;
    }
};

时间复杂度\(O(n)\),空间复杂度\(O(n)\)

方法二

该方法来自于力扣官方题解。
在此方法中,我们利用两个计数器 \(left\)\(right\) 。首先,我们从左到右遍历字符串,对于遇到的每个‘(’,我们增加 \(left\) 计数器,对于遇到的每个 ‘)’ ,我们增加 \(right\) 计数器。每当两个计数器值相等时,我们计算当前有效字符串的长度,并且记录目前为止找到的最长子字符串。当 \(right\) 计数器比 \(left\) 计数器大时,我们将两个计数器同时置0。这样的做法贪心地考虑了以当前字符下标结尾的有效括号长度,每次当右括号数量多于左括号数量的时候之前的字符我们都扔掉不再考虑,重新从下一个字符开始计算,但这样会漏掉一种情况,就是遍历的时候左括号的数量始终大于右括号的数量,即$ (()$ ,这种时候最长有效括号是求不出来的。
解决的方法也很简单,我们只需要从右往左遍历用类似的方法计算即可,只是这个时候判断条件反了过来:当 \(left\) 计数器比 \(right\) 计数器大时,我们将 \(left\)\(right\)计数器同时变回0。当两个计数器值相等时,我们计算当前有效字符串的长度,并且记录目前为止找到的最长子字符串。这样我们就能涵盖所有情况从而求解出答案。

class Solution {
public:
    int longestValidParentheses(string s) {
        int left = 0, right = 0, maxlength = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s[i] == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxlength = max(maxlength, 2 * right);
            } else if (right > left) {
                left = right = 0;
            }
        }
        left = right = 0;
        for (int i = (int)s.length() - 1; i >= 0; i--) {
            if (s[i] == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxlength = max(maxlength, 2 * left);
            } else if (left > right) {
                left = right = 0;
            }
        }
        return maxlength;
    }
};
posted on 2023-02-13 23:37  sc01  阅读(11)  评论(0编辑  收藏  举报