主题
题意解读
什么情况下是有效字符串?
可以从消消乐的角度理解,每次可以消除一对相邻的匹配括号,不断消除,如果可以把
比如
什么情况下是无效字符串?
- 左括号没有对应的右括号。例如
,缺失了一个右括号。 - 右括号没有对应的左括号。例如
,缺失了一个左括号。 - 括号类型不匹配。例如
,其中 要和 组成一对括号,但是括号类型不同。
思路
本题是「邻项消除」问题(见文末的题单),这类问题都可以用栈解决。
以
- 创建一个空栈。
- 从左到右遍历
。 ,这是一个左括号,入栈。 ,这是一个左括号,入栈。 ,这是一个左括号,入栈。 ,这是一个右括号,它必须和栈顶的 组成一对(消除),弹出栈顶。 ,这是一个右括号,它必须和栈顶的 组成一对(消除),弹出栈顶。 ,这是一个右括号,它必须和栈顶的 组成一对(消除),弹出栈顶。 - 遍历结束,由于栈为空,说明所有括号均已匹配完毕,返回
。反之,如果在遍历的过程中,发现栈为空,或者括号类型不匹配的情况,返回 。此外,如果遍历结束栈不为空,说明还有没匹配的左括号,返回 。
细节
- 由于括号两两一对,所以
的长度必须是偶数。如果 的长度是奇数,可以直接返回 。 - 我们可以创建一个哈希表(或者数组),保存每个右括号对应的左括号,这样可以直接判断栈顶的左括号是否与右括号为同一类型,从而省去大量 if-else 判断。
写法一
python
class Solution:
def isValid(self, s: str) -> bool:
if len(s) % 2: # s 长度必须是偶数
return False
mp = {')': '(', ']': '[', '}': '{'}
st = []
for c in s:
if c not in mp: # c 是左括号
st.append(c) # 入栈
elif not st or st.pop() != mp[c]: # c 是右括号
return False # 没有左括号,或者左括号类型不对
return not st # 所有左括号必须匹配完毕cpp
// C++ 版待补充cpp
class Solution {
unordered_map<char, char> mp = {{')', '('}, {']', '['}, {'}', '{'}};
public:
bool isValid(string s) {
if (s.length() % 2) { // s 长度必须是偶数
return false;
}
stack<char> st;
for (char c : s) {
// mp.contains(c) 用来判断 c 是不是 mp 的一个 key
if (!mp.contains(c)) { // c 是左括号
st.push(c); // 入栈
} else { // c 是右括号
if (st.empty() || st.top() != mp[c]) {
return false; // 没有左括号,或者左括号类型不对
}
st.pop(); // 出栈
}
}
return st.empty(); // 所有左括号必须匹配完毕
}
};写法二
也可以在哈希表/数组中保存每个左括号对应的右括号。在遍历到左括号时,把对应的右括号入栈。这样遍历到右括号时,只需看栈顶括号是否一样即可。
python
class Solution:
def isValid(self, s: str) -> bool:
if len(s) % 2: # s 长度必须是偶数
return False
mp = {'(': ')', '[': ']', '{': '}'}
st = []
for c in s:
if c in mp: # c 是左括号
st.append(mp[c]) # 入栈对应的右括号
elif not st or st.pop() != c: # c 是右括号
return False # 没有左括号,或者左括号类型不对
return not st # 所有左括号必须匹配完毕cpp
// C++ 版待补充cpp
class Solution {
unordered_map<char, char> mp = {{'(', ')'}, {'[', ']'}, {'{', '}'}};
public:
bool isValid(string s) {
if (s.length() % 2) { // s 长度必须是偶数
return false;
}
stack<char> st;
for (char c : s) {
if (mp.contains(c)) { // c 是左括号
st.push(mp[c]); // 入栈对应的右括号
} else { // c 是右括号
if (st.empty() || st.top() != c) {
return false; // 没有左括号,或者左括号类型不对
}
st.pop(); // 出栈
}
}
return st.empty(); // 所有左括号必须匹配完毕
}
};写法三
用 if-else 代替 mp。
python
class Solution:
def isValid(self, s: str) -> bool:
if len(s) % 2: # s 长度必须是偶数
return False
st = []
for c in s:
if c == '(':
st.append(')') # 入栈对应的右括号
elif c == '[':
st.append(']')
elif c == '{':
st.append('}')
elif not st or st.pop() != c: # c 是右括号
return False # 没有左括号,或者左括号类型不对
return not st # 所有左括号必须匹配完毕cpp
// C++ 版待补充cpp
class Solution {
public:
bool isValid(string s) {
if (s.length() % 2) { // s 长度必须是偶数
return false;
}
stack<char> st;
for (char c : s) {
if (c == '(') {
st.push(')'); // 入栈对应的右括号
} else if (c == '[') {
st.push(']');
} else if (c == '{') {
st.push('}');
} else { // c 是右括号
if (st.empty() || st.top() != c) {
return false; // 没有左括号,或者左括号类型不对
}
st.pop(); // 出栈
}
}
return st.empty(); // 所有左括号必须匹配完毕
}
};复杂度分析
- 时间复杂度:
,其中 是 的长度。 - 空间复杂度:
或 。如果能修改 ,那么直接把 当作栈,可以做到 额外空间。
相似题目
见 数据结构题单 中的【§3.3 邻项消除】。
分类题单
- 滑动窗口(定长/不定长/多指针)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/最短路/最小生成树/二分图/基环树/欧拉路径)
- 动态规划(入门/背包/状态机/划分/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心算法(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
欢迎关注 B站@灵茶山艾府