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

猜你喜欢

转载自blog.csdn.net/GreyBtfly/article/details/88090218