[Leetcode] 32. 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.

思路

        这种求极值的问题很多情况下都可以采用动态规划加以解决,本题也不例外。可是对dp的不同定义却可以导致非常不同的时间复杂度。这里提供几种不同的思路:

1、二维动态规划:dp[i][j]表示从i到j之间的字符串是否可以构成合法的括弧对,则递推公式为:

        1)如果dp[i+1][j-1] == true,并且s[i] == '(' && s[j] == ')',则dp[i][j] = true;

        2)如果存在i < k < j,使得dp[i][k] == true && dp[k+1][j] == true,则dp[i][j] = true。

        如果dp[i][j]为true,则可以更新当前得到的最长合法括号长度。可惜这种思路的时间复杂度高达O(n^3),空间复杂度为O(n^2),无法通过Leetcode上面的大数据。

2、一维动态规划+栈:利用栈来保证左括弧的数目不小于右括弧的数目(这是括弧对合法的必要条件)并且记录当前左括弧的位置。定义dp[i + 1]表示以i 为结尾的最长合法括弧对的长度。根据左右括弧匹配的性质,思路为:

        1)如果字符串中当前字符为‘(’,则将其位置入栈,代表这个位置需要有一个匹配的右括弧;

        2)如果字符串中当前字符为')',则检查栈:

              a)如果此时栈为空,说明当前段的字符串是不合法的;

              b)如果此时栈不为空,则说明当前的右括弧可以被匹配,并且dp[i+1] = (i - j + 1) + dp[j]。其中(i - j + 1)表示从当前字符到栈顶元素的长度,而dp[j]表示栈顶元素之前的合法子字符串的长度。该思路的时间复杂度为O(n),空间复杂度为O(n)。

3、一维动态规划:仔细推敲2的思路,发现实际上对于dp[i+1]而言,通过dp[i]可以推导出能够和s[i] == ')' 匹配的左括弧的索引值为(i - dp[i] - 1)。那么我们就不再需要开辟栈空间了,而是可以直接推导出dp[i+1]和dp[i]之间的递推关系:dp[i + 1] = dp[i - dp[i] - 1] + dp[i] + 2。该算法的时间和空间复杂度同2。

4、栈:事实上')'也是可以作为分界符入栈的。如果当前字符为')',则试图在栈中寻找和它匹配的'('的左括弧。如果找到相对应的左括弧,则首先弹栈(为什么要先弹栈? 考虑()()的情况),然后可以知道以‘)’结尾的合法括弧对的长度为st.empty() ? i + 1 : i - st.top()。而不以该')'结尾的合法最长合法括弧对的长度为在此前已经被记录下来了,所以此时只需要更新其值即可。一旦无法找到和当前')'匹配的左括弧,则将该')'入栈作为分界符,使得后来的右括弧无法再与当前栈中非栈顶的左括弧相匹配。这个思路确实很精妙,虽然 理论上时间复杂度和空间复杂度仍然为O(n),但事实上在大多数情况下空间复杂度会远小于O(n)。

代码

一维动态规划+栈:

class Solution {
public:
    int longestValidParentheses(string s) 
    {
        if(s.size() == 0) 
            return 0;  
        stack<int> st;  
        vector<int> dp(s.size() + 1, 0);
        int ret = 0, len= 0;  
        for(int i = 0; i < s.size(); i++)  
        {  
            if(s[i] == '(') 
            {
                st.push(i);  
            }
            else  
            {  
                if(!st.empty())
                {
                    dp[i + 1] = (i - st.top() + 1) + dp[st.top()];  
                    st.pop();  
                    ret = max(dp[i + 1], ret);  
                }
            }  
        }  
        return ret;  
    }
};
一维动态规划:

class Solution {
public:
    int longestValidParentheses(string s) 
    {
        int ret = 0;
        // dp[i] means the length of the longest valid parenthese that ends with s[i]
        vector<int> dp(s.size() + 1, 0);  
        for(int i = 1; i < s.size(); i++)  
        {  
            if(s[i]=='(') 
                continue;  
            // now we know s[i] == ')'
            // (i - dp[i] - 1) is the index of the '(' that will match with s[i]
            if(i - dp[i] - 1 >= 0 && s[i - dp[i] - 1] =='(')   
                dp[i + 1] = dp[i - dp[i] - 1] + dp[i] + 2;  
            ret = max(ret, dp[i + 1]);  
        }  
        return ret;  
    }
};
栈:

class Solution {
public:
    int longestValidParentheses(string s) 
    {
        int ret = 0;  
        stack<int> st;  
        for(int i = 0; i < s.size(); i++)  
        {  
            if(s[i]=='(') 
            {
                st.push(i); 
            }
            else    // s[i] == ')'
            {
                if(!st.empty() && s[st.top()]=='(')
                {
                    st.pop();  
                    ret = max(st.empty() ? i + 1 : i - st.top(), ret);
                }
                else
                {
                    st.push(i);
                }
            }
        }  
        return ret;  
    }
};




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值