LeetCode - 856. Score of Parentheses
1. 问题背景
今天我们要探讨的是 LeetCode 第 856 题 Score of Parentheses。这是一道关于括号字符串处理的经典问题,主要考察我们对栈结构的运用和对嵌套表达式的理解。
题目描述
给定一个平衡括号字符串S
,计算其分数,规则如下:
()
记为 1 分- 串联结构
AB
记为A + B
分(A、B 为合法括号子串)- 嵌套结构
(A)
记为2 * A
分(A 为合法括号子串)
示例
输入:"()"
输出: 1
输入:
"(())"
输出: 2输入:
"()()"
输出: 2
输入:
"(()(()))"
输出: 6
2. 解题思路分析
这个问题的核心在于如何根据括号的嵌套结构计算分数。我们可以通过栈结构来模拟嵌套层级,从而高效地解决这个问题。
2.1 核心方法:栈模拟
- a. 初始化栈:栈底初始值设为 0,代表当前层级的初始分数。
- b. 遍历字符:
- 遇到
(
时:压入 0,表示开启一个新的子层级。 - 遇到
)
时:弹出栈顶元素v
,并更新当前层级分数:- 若
v = 0
(即()
形式),当前层级加 1。 - 若
v > 0
(即(A)
形式),当前层级加2 * v
。
- 若
- 遇到
- c. 最终结果:栈底元素即为整个字符串的分数。
这种方法的时间复杂度是 O(n),其中 n 是字符串的长度,因为我们需要遍历字符串一次。空间复杂度是 O(n),最坏情况下栈的深度为 n/2。
3. JavaScript 解法实现
/**
* @param {string} S
* @return {number}
*/
var scoreOfParentheses = function(S) {
let stack = [0];
for (let char of S) {
if (char === '(') {
stack.push(0);
} else {
let v = stack.pop();
stack[stack.length - 1] += Math.max(2 * v, 1);
}
}
return stack[0];
};
4. Python 解法实现
class Solution:
def scoreOfParentheses(self, S: str) -> int:
stack = [0]
for char in S:
if char == '(':
stack.append(0)
else:
v = stack.pop()
stack[-1] += max(2 * v, 1)
return stack[0]
5. 复杂度分析
- 时间复杂度:O(n),需遍历字符串一次。
- 空间复杂度:O(n),最坏情况下栈深度为 n/2。
6. 算法解释
- 栈的作用:记录每个嵌套层级的当前分数。
- 分数计算:
- 当遇到
)
时,若栈顶为 0,说明直接闭合空括号,得 1 分。 - 若栈顶非 0,说明闭合的是嵌套结构,分数需乘以 2。
- 当遇到
- 示例演示:
- 输入
"( () ( () ) )"
- 栈变化过程:
[0] → [0,0] → [0,1] → [0,1,0] → [0,1,1] → [0,3] → [6]
- 输入
7. 解法优化与变种思考
上述解法已经是最优解,但我们可以通过数学方法进一步优化空间复杂度:
- 优化思路:统计核心括号的深度
观察发现,最终的分数其实是所有()
子串的深度的 2 次幂之和。例如:
"(())"
的分数为2^1 = 2
"()()"
的分数为2^0 + 2^0 = 2
"(()(()))"
的分数为2^1 + 2^2 = 2 + 4 = 6
优化后的代码:
var scoreOfParentheses = function(S) {
let ans = 0;
let depth = 0;
for (let i = 0; i < S.length; i++) {
if (S[i] === '(') {
depth++;
} else {
depth--;
if (S[i-1] === '(') {
ans += 1 << depth;
}
}
}
return ans;
};
这种优化后的方法空间复杂度为 O(1),只需要常数级的额外空间。
8. 总结
这道题展示了栈结构在处理嵌套表达式问题中的强大作用。通过栈,我们可以清晰地跟踪每个层级的计算状态,从而高效地解决问题。同时,我们还通过数学分析找到了空间复杂度更优的解法。
在解决类似的嵌套结构问题时,我们应该优先考虑栈这种数据结构,它能帮助我们理清层级关系,简化计算过程。希望这篇解析能帮助你更好地理解如何处理括号字符串相关的问题。
如果你有任何疑问或其他解法,欢迎在评论区留言讨论!