[LeetCode] Longest Valid Parentheses | 最长的合法括号子串

https://leetcode.com/problems/longest-valid-parentheses/?tab=Description

将longest valid parentheses简称为LVP。(下面的示例代码都是C++)

思路1:Brute Force

扫描字符串,对每个位置,搜索以该位置为起点的LVP,取最长的。

Time complexity: O(n^2)
Space complexity: O(1)

ps. 此方法会在leetcode最后一组数据TLE 😦

class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.size();
        if (n == 0) return 0;
        
        int ret = 0;
        for (int i = 0; i < n; ++i) {
            int l_cnt = 0, r_cnt = 0;
            int len = 0;
            for (int j = i; j < n; ++j) {
                if (s[j] == '(')
                    l_cnt++;
                else
                    r_cnt++;
                if (l_cnt < r_cnt)
                    break;
                else if (l_cnt == r_cnt)
                    len = j - i + 1;
            }
            ret = max(ret, len);
        }
        
        return ret;
    }
};

O(n^2)的不行,O(nlogn)的?比如,分治?分治的难点在于LVP可能在string的左半部分、右半部分、以及横跨左右两边,最后一种情况是最难处理的,比如字符串"(())()"分成两半就是"(()"")()",左、右两半的LVP都是1,而横跨的就比较难求。事实上,这个例子的最优解就是横跨左右两边的LVP。

思路2:Dynamic Programming

  • 定义f[i]表示以s[i]结尾的LVP的长度
  • 如果s[i] = '(', 显然f[i] = 0;
  • 如果s[i] = ')', 字符串到位置i大概是形如"...((..))"的样子,先看看尾巴是不是形如"((..))"这样的,再看这部分的前面是否有valid parentheses,有的话还要再加上。
    • 要判断尾巴是不是形如"((..))"这样,就须要知道左边界是不是'(',而左边界的位置可以根据f[i-1]推算出来(因为左右边界里面的长度正好是f[i-1]):idx = i - f[i-1] - 1
    • 然后再加上左边界的左边一位的LVP的长度,即f[idx-1]

Time complexity: 用DP的话,只要扫描一遍字符串,保留最大的f[i]即可,所以时间复杂度是O(n)
Space complexity: 由于DP的记忆深度无法确定(因为计算f[i]可能不仅要用到f[i-1],还要用到f[idx-1]),因此空间复杂度是O(n)

class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.size();
        if (n == 0) return 0;
        
        int ret = 0;
        int f[n];
        f[0] = 0;
        for (int i = 1; i < n; ++i) {
            f[i] = 0;
            if (s[i] == '(') {
                f[i] = 0;
            } else {
                int idx = i - f[i-1] - 1;
                if (idx >= 0 && s[idx] == '(') {  // detect "...((..))"
                    f[i] = f[i-1] + 2;
                    if (idx - 1 >= 0) f[i] += f[idx-1];
                }
            }
            ret = max(ret, f[i]);
        }
        
        return ret;
    }
};

思路3: Stack

类似这种括号匹配的问题,Stack和双指针等工具要作为备选方法。而这题是可以用栈解决的。

  • 开一个栈,存的是字符的下标(注意是下标,因为我们要计算长度)。从左到右扫描字符串,记当前位置为i:
  • 如果s[i] = '(', push下标
  • 如果s[i] = ')',看栈顶对应的是不是 '('
    • 是则说明我们发现了一个valid parentheses子串,pop栈顶,然后(i - 栈顶)就是这个子串的长度(注意栈的判空,空的话长度就是i+1);
    • 否则push下标
class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.size();
        if (n == 0) return 0;
        
        int ret = 0;
        stack<int> stk;  // to store the index!
        for (int i = 0; i < n; ++i) {
            if (s[i] == ')' && !stk.empty() && s[stk.top()] == '(') {
                stk.pop();
                if (stk.empty()) ret = max(ret, i + 1);
                else ret = max(ret, i - stk.top());
            } else {
                stk.push(i);
            }
        }
        
        return ret;
    }
};

Time complexity: O(n)
Space complexity: O(n)

思路4:Double Direction Scan

先正向扫描一遍字符串,用两个变量维护左、右括号的数量,当左括号数和右括号数相等时,记下发现的valid parentheses长度(数量*2),最后保存最大的;然后再反向扫描一遍,做同样的操作。

双向扫描的原因在于对数据的理解,如果只正向扫描一遍,当字符串是左括号远远比右括号多,可能直到扫描完都找不到一个valid parentheses,然而事实上却是存在的。比如"((((())",由于左括号很多,就把可行解覆盖了,正向扫描一遍无法找到这样的解。如果再反向扫描一遍,就一定可以找出来。

这个方法确实很巧妙。

Time complexity: 两次遍历也是O(n)
Space complexity: O(1)

class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.size();
        if (n == 0) return 0;
        
        int ret = 0;
        
        // 前向扫描
        int l_cnt = 0, r_cnt = 0;
        for (int i = 0; i < n; ++i) {
            if (s[i] == '(')
                l_cnt++;
            else
                r_cnt++;
            if (l_cnt < r_cnt)
                l_cnt = r_cnt = 0;  // 而今迈步从头越
            else if (l_cnt == r_cnt)
                ret = max(ret, l_cnt * 2);
        }
        
        // 反向扫描
        l_cnt = r_cnt = 0;
        for (int i = n - 1; i >= 0; --i) {
            if (s[i] == '(')
                l_cnt++;
            else
                r_cnt++;
            if (l_cnt > r_cnt)
                l_cnt = r_cnt = 0;
            else if (l_cnt == r_cnt)
                ret = max(ret, r_cnt * 2);
        }
        
        return ret;
    }
};
posted @ 2017-02-20 08:59  mioopoi  阅读(999)  评论(0)    收藏  举报