678. 有效的括号字符串
给你一个只包含三种字符的字符串,支持的字符类型分别是 '('
、')'
和 '*'
。请你检验这个字符串是否为有效字符串,如果是 有效 字符串返回 true
。
有效 字符串符合如下规则:
- 任何左括号
'('
必须有相应的右括号')'
。 - 任何右括号
')'
必须有相应的左括号'('
。 - 左括号
'('
必须在对应的右括号之前')'
。 '*'
可以被视为单个右括号')'
,或单个左括号'('
,或一个空字符串""
。
示例 1:
输入:s = "()" 输出:true
示例 2:
输入:s = "(*)" 输出:true
示例 3:
输入:s = "(*))" 输出:true
提示:
1 <= s.length <= 100
s[i]
为'('
、')'
或'*'
提示 1
Use backtracking to explore all possible combinations of treating '*' as either '(', ')', or an empty string. If any combination leads to a valid string, return true.
提示 2
DP[i][j] represents whether the substring s[i:j] is valid.
提示 3
Keep track of the count of open parentheses encountered so far. If you encounter a close parenthesis, it should balance with an open parenthesis. Utilize a stack to handle this effectively.
提示 4
How about using 2 stacks instead of 1? Think about it.
解法1: 栈
算法逻辑
算法的核心逻辑是使用栈来跟踪未匹配的括号和通配符。通过遍历字符串,我们可以逐步构建出有效的括号匹配序列。对于遇到的每个右括号')',我们应该按照以下顺序尝试匹配:
- 首先检查左括号栈是否为空。如果左括号栈不为空,这意味着有一个对应的左括号'('可以与当前的右括号')'匹配,我们将左括号栈顶的'('弹出,完成匹配。
- 如果左括号栈为空,接下来检查星号栈。如果星号栈也不为空,我们可以将星号栈顶的''视为一个左括号'(',完成匹配,并将''弹出星号栈。
- 如果左括号栈和星号栈都为空,这意味着没有可用的左括号或'*'来匹配当前的右括号')',因此字符串无效。
算法实现步骤
- 初始化两个空栈:一个用于存储未匹配的左括号'(',另一个用于存储遇到的'*'字符。
- 遍历字符串:对输入的字符串进行遍历,对于每个字符,执行以下操作:
- 如果字符是'(',将其压入左括号栈。
- 如果字符是'*',将其压入星号栈。
- 如果字符是')':
- 首先检查左括号栈是否为空。如果不为空,弹出左括号栈顶的'(',完成匹配。
- 如果左括号栈为空,检查星号栈是否为空。如果不为空,弹出星号栈顶的'*',将其视为左括号'('完成匹配。
- 如果两个栈都为空,返回false,因为无法找到匹配的括号。
- 完成遍历后检查栈:
- 如果星号栈不为空,我们可以将所有'*'视为空字符串。如果此时左括号栈也为空,字符串有效,返回true;否则,字符串无效,返回false。
- 如果星号栈为空,但左括号栈不为空,说明有未匹配的左括号,字符串无效,返回false。
Java版:
class Solution {
public boolean checkValidString(String s) {
// stack 记录未匹配的左括号的下标
Deque<Integer> stack = new ArrayDeque<>();
// key 记录未匹配的 * 的下标
Deque<Integer> key = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (ch == '(') {
stack.push(i);
} else if (ch == ')') {
if (!stack.isEmpty()) {
stack.pop();
} else if (!key.isEmpty()) {
key.poll();
} else {
return false;
}
} else {
key.offer(i);
}
}
while (!stack.isEmpty() && !key.isEmpty()) {
if (stack.peek() < key.peekLast()) {
stack.pop();
key.pollLast();
} else {
return false;
}
}
if (!stack.isEmpty()) {
return false;
}
return true;
}
}
Python3版:
class Solution:
def checkValidString(self, s: str) -> bool:
# stack 记录未匹配的左括号的下标
stack = []
# key 记录未匹配的 * 的下标
key = []
for i, ch in enumerate(s):
if ch == '(':
stack.append(i)
elif ch == ')':
if stack:
stack.pop()
elif key:
key.pop(0)
else:
return False
else:
key.append(i)
while stack and key:
if stack[-1] < key[-1]:
stack.pop()
key.pop()
else:
return False
if stack:
return False
return True
复杂度分析
- 时间复杂度:O(n),其中 n 是字符串 s 的长度。需要遍历字符串一次,遍历过程中每个字符的操作时间都是 O(1),遍历结束之后对左括号栈和星号栈弹出元素的操作次数不会超过 n。
- 空间复杂度:O(n),其中 n 是字符串 s 的长度。空间复杂度主要取决于左括号栈和星号栈,两个栈的元素总数不会超过 n。