LeetCode 32. 最长有效括号

leetcode acwing 题解

算法1

两遍线性扫描+贪心\(O(n)\)

线性扫描一遍字符串,设置sum = 0,假如是(,就+1,假如是),就-1;计算完之后判断一下当前的和,如果sum == 0说明是有效字符串,统计一下结果。如果是sum < 0说明)太多了,这种情况无论我们输入什么都没法形成有效匹配,如果是sum > 0,说明(太多了,这种情况我们输入),是可以生成有效匹配的。

但是只是这样的话,有一种情况会覆盖不到,那就是扫描到最后,sum > 0,但是字符串里确实有有效串。如...(((()((,最中间的有效匹配会匹配不到。所以我们逆向思维,将整个过程反着来一遍就行了。

时间复杂度

\(O(n)\)

空间复杂度

\(O(1)\)

class Solution {
public:
    int longestValidParentheses(string s) {
        if (s.empty()) return 0;

        int start = 0, sum = 0, res = 0;
        for (int i = 0; i < s.size(); i ++)
        {
            if (s[i] == '(') sum ++;
            else if (s[i] == ')') sum --;
            if (sum < 0) start = i + 1, sum = 0;
            else if (sum == 0) res = max(res, i - start + 1);
        }

        start = s.size()-1, sum = 0;
        for (int i = s.size()-1; i >= 0; i --)
        {
            if (s[i] == ')') sum ++;
            else if (s[i] == '(') sum --;
            if (sum < 0) start = i - 1, sum = 0;
            else if (sum == 0) res = max(res, start - i + 1);
        }

        return res;
    }
};

算法2

(动态规划)\(O(n)\)

image-20201224163235557

时间复杂度

\(O(n)\)

空间复杂度

\(O(n)\)

class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.size();
        if (!n) return 0;

        vector<int> f(n, 0);
        for (int i = 1; i < n; i ++)
        {
            if (s[i] == ')')
            {
                if (s[i-1] == '(') 
                {
                    if (i >= 2) f[i] = f[i-2] + 2;
                    else f[i] = 2;
                }
                else
                {
                    if (i-1-f[i-1] >= 0 && s[i-1-f[i-1]] == '(')
                        if (i - f[i-1] - 2 >= 0) f[i] = f[i-1] + 2 + f[i - f[i-1] - 2];
                        else f[i] = f[i-1] + 2;
                }
            }
        }

        int res = 0;
        for (int i = 0; i < n; i ++) res = max(res, f[i]);
        return res;
    }
};

Tip

写动规一定要注意转移方程、条件各个地方的下标!

算法3

\(O(n)\)

用栈来维护字符串中待匹配的(,用start来记录当前可能形成最长合法字串的起始位置。

  1. 当当前输入的字符串为(时,直接入栈
  2. 当输入),但是栈为空,说明不会再有输入能合法化目前的字符串,更新start = i + 1
  3. 当输入),但是栈不为空,栈顶元素出栈,若之后栈为空,说明找到了一个完全合法的子串,更新一下答案res=max(res, i - start + 1);若之后栈不为空,说明找到了一个部分合法的子串,更新一下答案为res = max(res, i - st.top())

时间复杂度

每个元素最多只会入栈出栈一次,因此是\(O(n)\)

空间复杂度

\(O(n)\)

class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.size();
        if (!n) return 0;

        stack<int> st;
        int start = 0, res = 0;
        for (int i = 0; i < n; i ++)
        {
            if (s[i] == '(') st.push(i);
            else
            {
                if (st.empty()) start = i + 1;
                else
                {
                    st.pop();
                    if (st.empty()) res = max(res, i - start + 1);
                    // 忘了
                    else res = max(res, i - st.top());
                } 
            }
        }

        return res;
    }
};

Tip

更进一步想,算法1是不是算法2更进一步思考的结果?算法2中栈的元素,假如我们也按算法1给它计算一下,是不是也是满足相应的关系?

posted @ 2020-12-24 17:11  alexemey  阅读(93)  评论(0)    收藏  举报