🌸题目
🍁给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:
- 任何左括号 ( 必须有相应的右括号 )。
- 任何右括号 ) 必须有相应的左括号 ( 。
- 左括号 ( 必须在对应的右括号之前 )。
- *可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符串。
- 一个空字符串也被视为有效字符串。
示例:
1 | 输入: "()" |
注意:字符串大小将在 [1,100] 范围内。
分析
这道题是经典的括号型,第一想到的应该是双栈
🌸解法一:DFS
- 先编写无 * 情况的代码
- count 记录左括号个数
- 遇左则增,遇右则减
- 若不够减则 return false
- 补充有 * 的情况
- 分别开出三种可能继续探索
- 任何一种成了即可
- 时间复杂度分析
- 代码结构涉及循环,可能有递归,故可用最好、最好、加权平均时间- 复杂度表示
- 若不考虑中途提前结束(递归终止 或 左括号不足匹配右括号的终止)
- 最好 O(n),无星号的情况
- 最坏 O(3^n^),全是星号的情况
- 若考虑中途提前结束
- 最好 O(1),右括号开头
最坏 O(3^n^),全星号的情况 - 求和各情况的 概率 * 此情况要遍历的元素个数
- 最好 O(1),右括号开头
1 | public boolean checkValidString(String s) { |
🌸解法二: 贪心
- 与方法一神似
- 无需每种情况都细分考虑,只需关注 左括号至少几个、至多几个
- 遇左则至多/至少都增加
- 遇右则至多/至少都减少
- 若至多
max
都不够抵右括号,则return false
- 若至多
- 遇 *则可能减少/不变/增多,即
min--
;max++
;- 若至少
min
小于 0 ,说明此分支走不通,需剪枝 - 类似方法一中的递归终止条件
if (count < 0) return false
;
- 若至少
- 最终要求左括号必须无剩余
min == 0;
min < 0
的情况在上一条思路中剪枝了
1 | public boolean checkValidString2(String s) { |
🌸解法三: 双向遍历
- 极端假设替换为全左或全右,双向遍历验证
- 假设所有
*
为(
- 因左括号必须在配对的左边,故从左向右遍历,看是否足够覆盖所有
')'
- 因左括号必须在配对的左边,故从左向右遍历,看是否足够覆盖所有
- 假设所有
*
都为')'
- 因右括号必须在配对的右边,故从右向左遍历,看是否足够覆盖所有
'('
- 因右括号必须在配对的右边,故从右向左遍历,看是否足够覆盖所有
java
1 | public boolean checkValidString3(String s) { |
🌸解法四: 双栈
- 括号匹配问题的经典解法
- 栈存放的是索引
- 一栈存左括号,一栈存星号
- 遍历过程中,同时判断是否有足够的右括号使他们出栈
- 优先抵消左括号(贪心思想)
- 两栈同时出栈并判断,要求所有左括号,都有 其右边索引的星号 能使其抵消
- 左括号不能还有富余
1 | public boolean checkValidString4(String s) { |