1. 程式人生 > 實用技巧 >LeetCode 32. 最長有效括號

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)\)

時間複雜度

\(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給它計算一下,是不是也是滿足相應的關係?