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里排除了)
}
};