LeetCode Week14: Longest Valid Parentheses

这周实现的还是Dynamic Programming的代码,这里选择一道比较经典的题目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.

我的分析

       刚开始拿到这个问题的时候,犯了一个错,就是认为是找有效的()对的长度,然后就想直接用栈来解决,结果后来提交的时候,有一个case是”()(()”,如果光按照匹配的方法,长度会是4,但是Expected Output = 2,这说明只有一对(),也就是说,这道题是要找的最长连续()

       考虑到这个情况,这个题我用了两种方法,两个方法的分析分别如下:

1、stack方法

       因为做括号匹配的时候,最开始就是想到用栈来解决,但是这个问题中需要求解的是最长连续(),那么使用栈的时候,就需要考虑当前栈中元素的位置,也就是说,我会设定一个Pair的结构体,其中包括了当前元素的位置idx(int类型)及元素的值(char类型)s。

假设现在需要判断的序列是”()(()”,那么我们的操作如下:

  • 如果当前元素是’(‘,那么直接将pair对(idx,’)’)放入栈中;
  • 如果当前元素是’)’,那么需要依据当前栈的内容来判断要做什么操作,假设现在遍历到序列的最后一个元素,也就是’)’:

      • 当前栈为空,那么可以把右扩号也放入栈中,这是为了帮助后面计算长度;
      • 当前栈不为空, 那么可以组成一对(),同时需要把栈顶的’(‘抛出栈。但是抛出栈之后,新的问题又出现了:这一对()之前还有没有连续的()对,我应该怎么来更新现在的最长的连续配对()?这个问题的解决就可以看现在的栈的状态来判断了。如果把配对的’(‘元素pop出栈后,栈为空,那么表示之前进栈的元素都已经跟组成了()对,并且当前这一对与前面的()对是连续,那么最长的()长度就应该变为当前序列的长度;如果此时栈的状态不为空,那么这一对()跟前面可能已经形成的()对就不连续了,那么就应该判断,现有的()的长度就是i-top().idx(这里并不是直接判断为2是因为存在(())的情况);
      • 之后可以判断是之前的长度更大,还是现在的()对更大,来动态更新最长()对的长度。

2、动态规划法
动态规划的过程就是把一个大问题划分成多个子问题来求解,这题是利用一个逆向思考的过程,思路是填充一个长度同样是length的数组dp,其中dp[i]表示从i到最后一为元素可以形成的最长的()的长度,从倒数第二个元素开始遍历(因为最后一个元素能形成的()对当然是0),如果当前元素是’(‘,那么填充这个数组的过程如下:

  • 看第i+1+dp[i+1]个元素是否是’)’,如果是’)’,那么就可以组成一对’)’;这里使用dp[i+1]的原因是,存在(())的情况,如果遍历到’(‘,那么要去找从它数下去的第3个元素来判断(因为内部有一对()要跳过它们),而对于内部的(),我们是可以很容易判断出当遍历到左边的’(‘时,可以形成的最长的()对的长度就是2,那么此时应该去找j = i+1+dp[i+1]位置,即跳过下一个位置能形成的最长()长度之后的位置的值来判断是否是’)’;

  • 当我们判断有一对合法的()时,我们还要看其后面的元素是否能与这一对形成一个连续的()对,如果’)’后面的元素的dp[j+1]不为0,表示后面也能形成连续的()对,当然dp[j+1]为0时就表明当前的这一对()与后面可能会有的连续()对是分开的。所以状态转移方程可以写作: dp[i]=dp[i+1]+2+dp[j+1],j+1<s.length()

代码

1、直接使用栈的方法分析得到的代码如下:

struct Pair{
    int idx;
    char s;
    Pair(int i,char j):idx(i),s(j){}
};
class Solution {
public:
    int longestValidParentheses(string s) {
        stackrec;
        int maxlen = 0;
        for(int i = 0; i < s.size();i++){
            // 如果是(,那么直接push进栈
            if(s[i] == '(') rec.push(Pair(i,'('));
            else if(s[i] == ')'){
                int cur = 0;// 现在的最长连续对
                // 栈里面有元素可以匹配成一对()
                if(!rec.empty() && rec.top().s == '(') 
                {
                    rec.pop();
                    cur = 0;
                    // 如果这时栈已经清空,那么这一次的()的长度要纳入前面的长度计算中
                    // 因为前面也有连续的()对
                    if(rec.empty())
                        cur = i+1;
                    else
                        // 栈未清空,前面残留有元素
                        // 说明这一对()与前面的()不连续,这时的长度只能是2
                        cur = i - rec.top().idx;
                }
                else{
                    rec.push(Pair(i,')'));
                }
                maxlen = max(maxlen,cur);
            }
        }
        return maxlen;
    }
};

2、 使用动态规划分析得到的代码如下:

class Solution {
public:
    int longestValidParentheses(string s) {
        int len = s.length();
        vector<int>dp(len,0);
        int maxlen = 0;
        for(int i = len-2; i >=0; i--){
            if(s[i] == '('){
                // 看下一个元素是否是')'
                int idx = i+1+dp[i+1]; // 考虑到(())的情况
                if(idx < len && s[idx] == ')')
                    // 如果下一个元素是')',那么就可以组成一对()
                    // 之后判断能够与后面的元素组成多长的连续()
                    dp[i] = (idx+1 < len)?2+dp[idx+1]+dp[i+1]:2+dp[i+1];
            }
            maxlen = max(dp[i],maxlen);
        }
        return maxlen;
    }
};

你可能感兴趣的:(LeetCode)