【medium(其實挺難的)】括號符匹配的幾個結論 Valid Parenthesis String

 

678

Valid Parenthesis String    

  32.1% Medium

Given a string containing only three types of characters: '(', ')' and '*', write a function to check whether this string is valid. We define the validity of a string by these rules:

  1. Any left parenthesis '(' must have a corresponding right parenthesis ')'.
  2. Any right parenthesis ')' must have a corresponding left parenthesis '('.
  3. Left parenthesis '(' must go before the corresponding right parenthesis ')'.
  4. '*' could be treated as a single right parenthesis ')' or a single left parenthesis '(' or an empty string.
  5. An empty string is also valid.

 

Example 1:

Input: "()"
Output: True

 

Example 2:

Input: "(*)"
Output: True

 

Example 3:

Input: "(*))"
Output: True

 

Note:

  1. The string size will be in the range [1, 100].

 

Accepted

23,844

Submissions

74,374

 

做了這個題以後,總結一下括號符匹配的三個結論。(證明過程有點囉嗦,若各位看官嫌棄的話可以直接看結論)

對於一個只包含左右括號的長度爲n的符號序列。若左右括號完美匹配,則一定滿足以下三個條件:

1.對於整個序列,左右括號數必定相等

2.對於任意前綴序列(任意的i滿足1<=i<=n)都滿足在區間[1,i]上,左括號數一定>=右括號數

3.與條件2同理,對於任意後綴序列,都滿足在區間[i,n]上 左括號數一定<=右括號數

簡單證明(說明)一下這三個推論。

因爲整個區間上完美匹配:

1.我們知道一個左括號一定匹配一個右括號,完美匹配的序列上兩者數量自然相等。(顯然成立)

2.(反證法)若在某一前綴[1,i]上左括號數<右括號數。考慮使用棧來從前往後匹配這個前綴的時候(遇到左括號直接入棧,遇到右括號則消掉一個棧內的左括號),由於右括號數比較多,一定會出現這種情況:遇到了右括號,但是棧內已經沒有了左括號可以消除它,則滿足括號符不匹配的條件。

3.由於括號匹配有對稱性(括號符匹配與數組正序倒序無關) ,則與2同理顯然成立。

由此可得,由完美匹配的條件可以推出3個條件。則完美匹配是3個條件的充分條件。

那可不可以根據3個條件推出完美匹配呢?

依然是考慮用棧來匹配字符串的過程,遍歷數組依次入棧的過程就相當於取任意前綴序列的過程。只要在任意前綴序列中右括號都能被消掉,則可以繼續匹配。若掃描完畢所有字符,棧內不殘留左括號,則整個數組匹配。

上面一段話說明:若滿足1,2條件,則可以推出完美匹配,得到反向的充分條件。故綜上所述:1,2條件是完美匹配的充分必要條件。

同理,1,3條件也是完美匹配的充分必要條件。

那2,3條件能不能推出完美匹配呢?

如果2成立,則我們可以知道在任意前綴序列中,左括號數>=右括號數。由於整個序列是最大的前綴序列,所以在整個序列中可以得到左括號數>=右括號數的結論。

如果3成立,我們同理可以得到左括號數<=右括號數。

所以:若2,3成立,一定可以同時得到兩個結論:

左括號數>=右括號數

左括號數<=右括號數

要同時滿足這兩個結論,那左右括號數只能相等。也就是可以推出條件1.

又因爲1,2條件是完美匹配的充要條件,2,3能推出1,所以2,3也能推出完美匹配。

所以:2,3也是完美匹配的充分必要條件。

結論:

已知完美匹配,則條件1,2,3成立。

而1,2,3條件只要任意兩條成立,則完美匹配。

 

本題則利用條件2,3推出完美匹配的結論來做。具體請看我加了註釋的代碼(第二份代碼)。

這是人家的最優的方法:

class Solution {
    public boolean checkValidString(String s) {
       int lo = 0, hi = 0;
       for (char c: s.toCharArray()) {
           lo += c == '(' ? 1 : -1;
           hi += c != ')' ? 1 : -1;
           if (hi < 0) break;
           lo = Math.max(lo, 0);
       }
       return lo == 0;
    }
}

這是我根據大概原理改的、加了註釋

class Solution {
public:
    bool checkValidString(string s) {
        int t=0;
        for(int i=0;i<s.length();i++)
        {
            if(s[i]==')')
                t--;
            else t++;
            if(t<0)
                return 0; //任意前綴區間裏:右括號比(左括號  +  *號)多,一定不匹配。
        }
        t=0;
        for(int i=0;i<s.length();i++)
        {
            /*
            只要(右括號 +  *號)多於左括號,就一定可以通過將*刪除或者把*變爲左括號的手段使括號匹配。
            (除非右括號比左括號+星號都多,但是這個情況已經被第一個for排除了)
            */
            if(s[i]=='(')  
                t++;
            else t--;   //右括號和星號都用來抵消左括號
            if(t<0)
                t=0;    //如果星號變爲右括號後使右括號比左括號多,那麼通過撤銷操作、將部分星號刪除,將部分星號變爲左括號可以使前綴平衡。
            //如果t==0 說明括號已經平衡了。
            //如果t>0  說明星號+右括號太少,左括號太多了。
        }
        if(t==0)    //最終平衡了
            return 1;
        return 0;   //t>0說明左括號太多。(右括號太多的情況在第一個for裏排除了)
    }
    
};

 

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