[LeetCode] 678. Valid Parenthesis String
Given a string s
containing only three types of characters: '('
, ')'
and '*'
, return true
if s
is valid.
The following rules define a valid string:
- 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""
.
Example 1:
Input: s = "()" Output: true
Example 2:
Input: s = "(*)" Output: true
Example 3:
Input: s = "(*))" Output: true
Constraints:
1 <= s.length <= 100
s[i]
is'('
,')'
or'*'
.
有效的括号字符串。
给你一个只包含三种字符的字符串,支持的字符类型分别是 '('、')' 和 '*'。请你检验这个字符串是否为有效字符串,如果是有效字符串返回 true 。
有效字符串符合如下规则:
任何左括号 '(' 必须有相应的右括号 ')'。
任何右括号 ')' 必须有相应的左括号 '(' 。
左括号 '(' 必须在对应的右括号之前 ')'。
'*' 可以被视为单个右括号 ')' ,或单个左括号 '(' ,或一个空字符串。
一个空字符串也被视为有效字符串。来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/valid-parenthesis-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题意是给一个字符串,里面有左括号右括号和星号,请判断 input 字符串是否是一个合法的括号组合。其中星号是一个通配符,可以表示左括号右括号或者星号。做这个题之前需要先复习一下20题。这道题我提供三种做法,第一种扫描两遍,第二种扫描一遍,第三种会用到栈。
首先是扫描两遍的做法。第一遍从左往右,第二遍从右往左。第一遍扫描的时候,把星号当做左括号,所以遇到左括号和星号的时候就 left++,遇到右括号就 left--;过程中只要 left 小于 0 就 return false,说明右括号多而且是星号没法补救的。第一遍扫描完毕之后如果 left == 0 则直接 return true,因为左括号 + 星号可以和右括号互相抵消。如果 left 大于0 也不用担心,因为这里面应该是存在一些星号的。
第二遍扫描的时候是从右往左,此时把星号当做右括号,如果看到右括号或者星号就 right++,看到左括号就 right--;跟第一遍一样,过程中只要 right 小于 0 了就 return false,说明左括号多到无法补救。遍历结束则 return true。
一个疑问:如果第一遍里面只是左括号比较多,left 不也是大于 0 吗?没问题,第二遍扫描的时候,这种 case 会被抓出来(因为 right 会小于 0 了)从而 return false。
时间O(n)
空间O(1)
Java实现
1 class Solution { 2 public boolean checkValidString(String s) { 3 int left = 0; 4 int right = 0; 5 int n = s.length(); 6 // scan from left to right 7 for (int i = 0; i < n; i++) { 8 if (s.charAt(i) == '(' || s.charAt(i) == '*') { 9 left++; 10 } else { 11 left--; 12 } 13 if (left < 0) { 14 return false; 15 } 16 } 17 if (left == 0) { 18 return true; 19 } 20 21 // scan from right to left 22 for (int i = n - 1; i >= 0; i--) { 23 if (s.charAt(i) == ')' || s.charAt(i) == '*') { 24 right++; 25 } else { 26 right--; 27 } 28 if (right < 0) { 29 return false; 30 } 31 } 32 return true; 33 } 34 }
JavaScript实现
1 /** 2 * @param {string} s 3 * @return {boolean} 4 */ 5 var checkValidString = function(s) { 6 let left = 0; 7 let right = 0; 8 let len = s.length; 9 for (let i = 0; i < len; i++) { 10 if (s.charAt(i) == '(' || s.charAt(i) == '*') { 11 left++; 12 } else { 13 left--; 14 } 15 if (left < 0) { 16 return false; 17 } 18 } 19 if (left == 0) { 20 return true; 21 } 22 23 for (let i = len - 1; i >= 0; i--) { 24 if (s.charAt(i) == ')' || s.charAt(i) == '*') { 25 right++; 26 } else { 27 right--; 28 } 29 if (right < 0) { 30 return false; 31 } 32 } 33 return true; 34 };
接下来是扫描一遍的做法。这里我们设置两个变量cmax和cmin,代表需要被match的左括号的上限和下限。
当遇到一个左括号的时候,cmax和cmin都需要++,因为你需要为这个左括号找一个配对
当遇到一个右括号的时候,自然是cmax--,cmin也需要--,但是如果cmin小于0了,说明右括号更多了,此时我们为了不让他小于0,我们取的是0和cmin的较大值
当遇到一个星号的时候,因为他是通配符,所以如果当做左括号的话,cmax就++,如果当做右括号的话,cmin就--,因为他可以去抵消一个左括号
最后判断的时候,如果cmax小于0就return false,因为cmax只有在遇到右括号的时候才--,说明右括号多到无法被抵消。
时间O(n)
空间O(1)
Java实现
1 class Solution { 2 public boolean checkValidString(String s) { 3 int cmin = 0; 4 int cmax = 0; 5 for (int i = 0; i < s.length(); i++) { 6 char c = s.charAt(i); 7 if (c == '(') { 8 cmax++; 9 cmin++; 10 } else if (c == ')') { 11 cmax--; 12 cmin = Math.max(cmin - 1, 0); 13 } else { 14 cmax++; 15 cmin = Math.max(cmin - 1, 0); 16 } 17 if (cmax < 0) { 18 return false; 19 } 20 } 21 return cmin == 0; 22 } 23 }
第三种解法用到栈。这里我们需要两个栈,一个记录左括号的 index,一个记录星号的 index,这里我们分别叫做 stack1 和 stack2。
- 遇到左括号,把 index 入栈 stack1
- 遇到右括号,
- 如果 stack1 不为空,弹出栈顶元素,说明有一个左右括号的配对
- 如果 stack1 为空,看看 stack2 是否为空 - 是否有星号,如有,则用星号抵消左括号
- 如果此时 stack1 和 stack2 都为空,return false
- 遇到星号,放入 stack2
此时我们可以处理所有左括号,但是 stack1 和 stack2 可能不为空,此时我们需要判断 stack2 里面的星号是否能抵消 stack1 里剩余未配对的括号。注意这里配对的原则是如果括号的 index 大于星号的 index,说明这个括号是不能被抵消的,因为他出现在了一个星号之后,星号只能抵消之前的括号。
时间O(n)
空间O(n)
Java实现
1 class Solution { 2 public boolean checkValidString(String s) { 3 Stack<Integer> operators = new Stack<>(); 4 Stack<Integer> stars = new Stack<>(); 5 for (int i = 0; i < s.length(); i++) { 6 // 遇到左括号 7 if (s.charAt(i) == '(') { 8 operators.push(i); 9 } 10 // 遇到右括号 11 else if (s.charAt(i) == ')') { 12 // 如果有左括号就抵消 13 if (operators.size() > 0) { 14 operators.pop(); 15 } 16 // 如果有星号就抵消 17 else if (stars.size() > 0) { 18 stars.pop(); 19 } else { 20 return false; 21 } 22 } 23 // 星号 24 else { 25 stars.push(i); 26 } 27 } 28 29 while (operators.size() > 0 && stars.size() > 0) { 30 if (operators.peek() > stars.peek()) { 31 return false; 32 } 33 operators.pop(); 34 stars.pop(); 35 } 36 return operators.size() == 0; 37 } 38 }
相关题目
1111. Maximum Nesting Depth of Two Valid Parentheses Strings
921. Minimum Add to Make Parentheses Valid