32. 最长有效括号

148 阅读3分钟

题目介绍

力扣32题:leetcode-cn.com/problems/lo…

image.png

动态规划

1.定义dp数组

首先,我们定义一个 dp 数组,其中第 i 个元素表示以下标为 i 的字符结尾的最长有效子字符串的长度。

2.确定状态:

对于最优的策略,一定有最后一个元素 s[i].

所以,我们先看第 i 个位置,这个位置的元素s[i]可能有如下两种情况:

  • s[i] == '(':这时,s[i] 无法和其之前的元素组成有效的括号对,所以,dp[i] = 0。

  • s[i] == ')':这时,需要看其前面对元素来判断是否有有效括号对。

    • 情况1:s[i - 1] == '(' ,即 s[i]s[i−1] 组成一对有效括号,有效括号长度新增长度2,i位置对最长有效括号长度为 其之前2个位置的最长括号长度加上当前位置新增的2,我们无需知道i−2位置对字符是否可以组成有效括号对。那么有:dp[i] = dp[i - 2] + 2

    • 情况2:s[i - 1] == ')',这种情况下,如果前面有和s[i]组成有效括号对的字符,即形如( (....) )((....)),这样的话,就要求s[i - 1]位置必然是有效的括号对,否则s[i]无法和前面对字符组成有效括号对。这时,我们只需要找到和s[i]配对对位置,并判断其是否是 ( 即可。和其配对对位置为:i - dp[i - 1] - 1,如果:s[i - dp[i - 1] - 1] == '(' ,有效括号长度新增长度2,i位置对最长有效括号长度为 i-1位置的最长括号长度加上当前位置新增的2,那么有:dp[i] = dp[i - 1] + 2

值得注意的是,i - dp[i - 1] - 1i 组成了有效括号对,这将是一段独立的有效括号序列,如果之前的子序列是形如 (...)(...) 这种序列,那么当前位置的最长有效括号长度还需要加上这一段。所以:dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2

image.png

举个例子:)()(()),此时i = 6dp[i - 1] = dp[5] = 2 i - dp[i - 1] - 1 = 3i = 3的位置刚好是'(',此时dp[6] = dp[5] + 2 = 4 ?,但这是不正确的,因为最左边还有一对括号!!!,所以:dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2

3. 状态转移方程

if s[i] == '(' :
    dp[i] = 0
if s[i] == ')' :
    if s[i - 1] == '(' :
        dp[i] = dp[i - 2] + 2 #要保证i - 2 >= 0

    if s[i - 1] == ')' and s[i - dp[i - 1] - 1] == '(' :
        dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2 #要保证i - dp[i - 1] - 2 >= 0

代码如下:

class Solution {
    public int longestValidParentheses(String s) {
        int n = s.length();
        int[] dp = new int[n];//dp是以i处括号结尾的有效括号长度
        int max_len = 0;
    //i从1开始,一个是单括号无效,另一个是防i - 1索引越界
        for(int i = 1; i < n; i++) {
            if(s.charAt(i) == ')') { //遇见右括号才开始判断
                if(s.charAt(i - 1) == '(') { //上一个是左括号
                    if(i < 2) { //开头处
                        dp[i] = 2;
                    } else { //非开头处
                        dp[i] = dp[i - 2] + 2;
                    }
                }
                else { //上一个也是右括号
                    if(dp[i - 1] > 0) {//上一个括号是有效括号
//pre_left为i处右括号对应左括号下标,推导:(i-1)-dp[i-1]+1 - 1
                        int pre_left = i - dp[i - 1] - 1;
                        if(pre_left >= 0 && s.charAt(pre_left) == '(') {//左括号存在且为左括号(滑稽)
                            dp[i] = dp[i - 1] + 2;
                            //左括号前还可能存在有效括号
                            if(pre_left - 1 > 0) {
                                dp[i] = dp[i] + dp[pre_left - 1];
                            }
                        }
                    }
                }
            }
            max_len = Math.max(max_len, dp[i]);
        }
        return max_len;
    }
}

复杂度计算:

  • 时间复杂度: 遍历了一遍字符串,所以时间复杂度是:O(N)

  • 空间复杂度:需要和字符串长度相同的数组保存每个位置的最长有效括号长度,所以空间复杂度是:O(N)