678 | 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:
- Any left parenthesis
'('
must have a corresponding right parenthesis')'
. - Any right parenthesis
')'
must have a corresponding left parenthesis'('
. - Left parenthesis
'('
must go before the corresponding right parenthesis')'
. '*'
could be treated as a single right parenthesis')'
or a single left parenthesis'('
or an empty string.- An empty string is also valid.
Example 1:
Input: "()"
Output: True
Example 2:
Input: "(*)"
Output: True
Example 3:
Input: "(*))"
Output: True
Note:
- 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裏排除了)
}
};