题目描述
给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:
任何左括号 ( 必须有相应的右括号 )。
任何右括号 ) 必须有相应的左括号 ( 。
左括号 ( 必须在对应的右括号之前 )。
* 可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符串。
一个空字符串也被视为有效字符串。
样例
示例 1:
输入: "()"
输出: True
示例 2:
输入: "(*)"
输出: True
示例 3:
输入: "(*))"
输出: True
注意:
字符串大小将在 [1,100] 范围内。
算法1
(前后枚举) $O(n)$
由于星号可以当括号用,那么问题是星号什么时候都能当括号来用?
这里有个比较trick的地方,我们就正反各遍历一次,正向遍历的时候,我们把星号都当成左括号,
此时用经典的验证括号的方法,即遇左括号计数器加1,遇右括号则自减1,如果中间某个时刻计数器小于0了,直接返回false。如果最终计数器等于0了,我们直接返回true,因为此时我们把星号都当作了左括号,可以跟所有的右括号抵消。
而此时就算计数器大于0了,我们暂时不能返回false,因为有可能多余的左括号是星号变的,星号也可以表示空,所以有可能不多。
我们还需要反向遍历一下,这是我们将所有的星号当作右括号,遇右括号计数器加1,遇左括号则自减1,如果中间某个时刻计数器小于0了,直接返回false。遍历结束后直接返回true,
这是为啥呢?此时计数器有两种情况,要么为0,要么大于0。为0不用说,肯定是true,为啥大于0也是true呢?因为之前正向遍历的时候,我们的左括号多了,
我们之前说过了,多余的左括号可能是星号变的,也可能是本身就多的左括号。本身就多的左括号这种情况会在反向遍历时被检测出来,如果没有检测出来,说明多余的左括号一定是星号变的。
而这些星号在反向遍历时又变做了右括号,最终导致了右括号有剩余,所以当这些星号都当作空的时候,左右括号都是对应的,即是合法的。
你可能会有疑问,右括号本身不会多么,其实不会的,如果多的话,会在正向遍历中被检测出来。
图例展示判断的基本逻辑分支:
Java 代码
class Solution {
public boolean checkValidString(String s) {
int lcnt = 0;
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (ch == '(' || ch == '*') {
lcnt++;
} else {
lcnt--;
}
if (lcnt < 0) {
return false;
}
}
if (lcnt == 0) {
return true;
}
int rcnt= 0;
for (int i = s.length() - 1; i >= 0; i--) {
char ch = s.charAt(i);
if (ch == ')' || ch == '*') {
rcnt++;
} else {
rcnt--;
}
if (rcnt < 0) {
return false;
}
}
return true;
}
}