LeetCode-32.最长有效括号(考察点:栈/动态规划)

给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。

示例 1:

输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"

示例 2:

输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"

解法1:栈

类似于之前LeetCode20-有效的括号(该题的解法不再详述,大致思路:遇左括号入栈,遇右括号,先判断栈是否为空,若栈为空,则直接return false,若栈不为空,则出栈,继续处理下一字符;字符串结束时,若栈为空,return true,否则return false),可以用栈来解决。

但是做题的时候,发现了一个难点,即示例2的情况,第二个字符和第三个字符是一对有效的括号,此有效括号子串长度为2,第四个字符和第五个字符也是一对有效的括号,且这两个有效括号子串是挨着的,返回的结果应该是这两个有效括号子串长度之和。

用下面这个例子来看更直观一点:

下标3-14是一个有效括号子串,长度为12;15-16也是一个有效括号子串,长度为2;17-20是一个有效括号子串,长度为4,但最长有效括号子串长度并不是12,也不是12+2+4,而是下标2-21的有效括号子串,长度为20。

所以此种做法的难度在于如何判断两个有效括号子串是否直接相邻,若直接相邻,则最长有效括号子串需要将这两个相邻的子串包含进来,并且左右边界需往外扩展,如上例中,下标2和下标21所处位置的字符其实也是在最长有效括号子串中的。

解题思路:

1.需有一个变量start记录有效括号子串的起始下标,max表示最长有效括号子串长度,初始值均为0

2.遍历给字符串中的所有字符

    2.1若当前字符s[index]为左括号'(',将当前字符下标index入栈(下标稍后有其他用处),处理下一字符

    2.2若当前字符s[index]为右括号')',判断当前栈是否为空

        2.2.1若栈为空,则start = index + 1,处理下一字符(当前字符右括号下标不入栈)

        2.2.2若栈不为空,则出栈(由于仅左括号入栈,则出栈元素对应的字符一定为左括号,可与当前字符右括号配对),判断栈是否为空

            2.2.2.1若栈为空,则max = max(max, index-start+1)

            2.2.2.2若栈不为空,则max = max(max, index-栈顶元素值)

Java代码:

class Solution {
    public int longestValidParentheses(String s) {
        int max = 0, start = 0;
        if(null == s) return 0;

        int len = s.length();

        Stack<Integer> stack = new Stack<>();
        for(int index = 0; index < len; index++){
            //遇左括号(,压栈(栈中元素为当前位置所处的下标)
            if('(' == s.charAt(index)){
                stack.push(index);
                continue;
            } else {
                if(stack.isEmpty()){
                    start = index+1;
                    continue;
                } else {
                    stack.pop();
                    if(stack.isEmpty()){
                        max = Math.max(max, index-start+1);
                    } else {
                        max = Math.max(max, index-stack.peek());
                    }
                }
            }
        }

        return max;
    }
}

解法2:动态规划

需用到辅助数组d[s.length()],表示从当前字符开始,到字符串结尾的最长有效括号子串长度(当前字符需为有效括号子串的第一个字符)

解题思路:从字符串结尾往前处理,求辅助数组d[]

当前字符下标为index,若当前字符为左括号'(',判断index+1+d[index+1]位置的字符是否为右括号')',若为右括号,则d[index] = d[index+1]+2,并且判断index+1+d[index+1]+1位置的元素是否存在,若存在,则d[index] += d[index+1+d[index+1]+1](解决上述两个有效括号子串直接相邻的情况)

Java代码:

class Solution {
    public int longestValidParentheses(String s) {
        if(null == s) return 0;

        int len = s.length(), max = 0;
        int[] d = new int[len];

        for(int index = len-2; index >= 0; index--){
            int symIndex = index+1+d[index+1];
            if('(' == s.charAt(index) && symIndex < len && ')' == s.charAt(symIndex)){
                d[index] = d[index+1]+2;
                if(symIndex+1 < len){
                    d[index] += d[symIndex+1];
                }
            }

            max = Math.max(max, d[index]);
        }
        return max;
    }
}

动态规划思想:将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

类似问题还有LeetCode5-最长回文子串,有一种解法是Mancher 算法,也用到了类似的思想。


展开阅读全文

没有更多推荐了,返回首页