【LeetCode】32.Longest Valid Parentheses 最長合法序列

一、概述

輸入一個只包含左括號和右括號的字符串,輸出其最長合法序列的長度。

什麼叫最長合法序列呢?就是在此序列中,不改變順序,每一個左括號的後面總有一個右括號與之對應。例如

((()))

()()()

但是

))((

這種不是。

 

二、分析

1、我的思路

最開始我的思路類似之前寫Valid Parentheses一樣,用棧保存左括號,有一個有括號就出棧一個。但是問題來了:

如果只有一個合法子序列,那麼:

左括號剩餘,那右括號數量*2就是合法序列長度;右括號剩餘,那麼左括號*2就是合法序列長度。

但是如果不止一個合法子序列呢?

我們該如何延長一個合法子序列的長度呢?

我想到這樣一種方法:

已經有一對(),現在又出現了(,往後如果這個()能作爲子序列頭,則這個)後面的左括號壓入一個新棧,然後彈出,直到棧空,這樣就可以延長這一子序列的長度。

但是太麻煩了。有好多子序列得多少棧啊。邊界條件也難以界定。

於是我選擇了一個十分直觀的方法:

多次遍歷輸入序列,每次遇到(),把它們變成**,如果遇到*,那麼接着往下,無視它。

直到最後所有的()都變成**。

然後找到最長的*子序列,其長度就是結果。思路很簡單,因此實現也很簡單。代碼如下:

class Solution {
public:
    int longestValidParentheses(string s) {
        int len=s.size();
        if(len<=1)
            return 0;
        int num=-1;
        int now=0;
        int first=-1;
        while(num<now)
        {
            num=now;
            first=-1;
            for(int i=0;i<len;++i)
            {
                if(s[i]=='(')
                {
                    first=i;
                }
                else if(first!=-1&&s[i]==')')
                {
                    s[first]='*';
                    first=-1;
                    s[i]='*';
                    now=now+2;
                }
                else
                    continue;
            }
        }
        int sum=0;
        int max=0;
        for(int i=0;i<len;++i)
        {
            if(s[i]=='*')
            {
                ++sum;
            }
            else
            {
                if(max<sum)
                    max=sum;
                sum=0;
            }
        }
        if(max<sum)
            return sum;
        return max;
    }
};

有幾個地方要注意,每次進入循環的時候,要先更新當前的最大長度,重置first。

別看時空複雜度好像很不錯,但是我這是重複提交四次選最好的,這玩意太不準了:

2、較好的算法

我想不出,只是在底下喊666的份兒。但是看還是能看明白的:

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

他用了一個棧,並且只循環了一次。這個棧中保存什麼呢?保存的元素可以按如下形式描述:

如果循環在當前中斷,棧中元素都是沒能匹配的廢物元素,是一羣單身狗。

舉例子更恰當:

序列爲) ( ) ( ) ) ( ) ( ) (

首先 ) 入棧;然後 ( 入棧;然後判斷可知 ) 可以和棧頂的 ( 匹配。如果到這裏循環中斷,第一個 ) 就是單身狗。

但是沒中斷,因此目前棧頂的 ( 彈出;每次執行彈出操作後都會更新子序列長度的最大值。

這也就是該算法的核心:

子序列如何劃分?什麼樣的元素能夠把子序列劃分開?

答案就是廢物元素。也就是,當前的棧頂元素就是當前子序列的開頭,當前循環到的元素就是當前子序列的末尾。這很重要,可能有點難理解。

然後 ( 入棧。。。。。。

最後棧裏面剩什麼呢?剩下 ) ) ( 正好是三個廢物。它們在主序列中劃分出兩個子序列,長度都是4。因此結果爲4。

我覺得,能夠想出這個思路,要對問題有一個清晰的認識:

找最長合法子序列->子序列是什麼?->如何劃分子序列?->歸納推理,保存子序列的起點,動態更新終點,也就動態更新了長度。

自嘆弗如。

三、總結

多觀察,多思考,多做題。沒別的了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章