题目
标题和出处
标题:括号的分数
出处:856. 括号的分数
难度
6 级
题目描述
要求
给定一个平衡括号字符串 s \texttt{s} s,返回字符串的分数。
平衡括号字符串的分数按下述规则计算:
- "()" \texttt{"()"} "()" 得 1 \texttt{1} 1 分。
- AB \texttt{AB} AB 得 A + B \texttt{A} + \texttt{B} A+B 分,其中 A \texttt{A} A 和 B \texttt{B} B 是平衡括号字符串。
- (A) \texttt{(A)} (A) 得 2 × A \texttt{2} \times \texttt{A} 2×A 分,其中 A \texttt{A} A 是平衡括号字符串。
示例
示例 1:
输入:
s
=
"()"
\texttt{s = "()"}
s = "()"
输出:
1
\texttt{1}
1
示例 2:
输入:
s
=
"(())"
\texttt{s = "(())"}
s = "(())"
输出:
2
\texttt{2}
2
示例 3:
输入:
s
=
"()()"
\texttt{s = "()()"}
s = "()()"
输出:
2
\texttt{2}
2
示例 4:
输入:
s
=
"(()(()))"
\texttt{s = "(()(()))"}
s = "(()(()))"
输出:
6
\texttt{6}
6
数据范围
- 2 ≤ s.length ≤ 50 \texttt{2} \le \texttt{s.length} \le \texttt{50} 2≤s.length≤50
- s \texttt{s} s 只包含 ‘(’ \texttt{`('} ‘(’ 和 ‘)’ \texttt{`)'} ‘)’
- s \texttt{s} s 是平衡括号字符串
解法一
思路和算法
由于给定的字符串 s s s 是平衡括号字符串,因此每个左括号都有一个右括号匹配。根据分数计算的规则,分数和嵌套层数以及原语得分有关。原语的含义是一个非空平衡字符串,不能拆分成两个平衡字符串拼接的形式。
为了计算括号的分数,可以使用栈存储分数,每次遇到右括号时根据栈内元素计算分数。
从左到右遍历字符串 s s s,遇到左括号则将 0 0 0 入栈,遇到右括号时,可能有两种情况:
-
前一个字符是左括号,此时当前右括号是 “()" \text{``()"} “()" 的一部分,得分为 1 1 1;
-
前一个字符是右括号,则前一个右括号已经计算了分数且入栈,此时前一个括号在内层,当前右括号在外层,外层括号对分数的影响是将内层括号的分数之和乘以 2 2 2。
上述两种情况都可以使用如下做法计算当前右括号处的分数:依次将栈内元素出栈,直到遇到元素 0 0 0,元素 0 0 0 为与当前右括号匹配的左括号(因为内层的左括号都已经和右括号匹配,所以内层的左括号对应的 0 0 0 都已经出栈),计算出栈元素之和 score \textit{score} score,然后计算当前右括号处的分数。根据 score \textit{score} score 的值,计算方法分别如下:
-
如果 score = 0 \textit{score} = 0 score=0,则对应上述情况 1,因此当前右括号处的分数为 1 1 1;
-
如果 score > 0 \textit{score} > 0 score>0,则对应上述情况 2,因此当前右括号处的分数为 score × 2 \textit{score} \times 2 score×2。
计算得到当前右括号处的分数之后,将该分数入栈。
遍历结束之后,栈内的元素为字符串 s s s 中的每个原语的分数,计算栈内的元素之和,即为字符串 s s s 的分数。
考虑如下括号字符串: s = “()(()(()))" s = \text{``()(()(()))"} s=“()(()(()))"。该字符串的长度为 10 10 10,计算分数的过程如下。
下标 0 0 0:字符为 ‘(’ \text{`('} ‘(’,将 0 0 0 入栈,此时栈为 [ 0 ] [0] [0],其中左边为栈底,右边为栈顶。
下标 1 1 1:字符为 ‘)’ \text{`)'} ‘)’,将 0 0 0 出栈,将 1 1 1 入栈,此时栈为 [ 1 ] [1] [1]。
下标 2 2 2:字符为 ‘(’ \text{`('} ‘(’,将 0 0 0 入栈,此时栈为 [ 1 , 0 ] [1, 0] [1,0]。
下标 3 3 3:字符为 ‘(’ \text{`('} ‘(’,将 0 0 0 入栈,此时栈为 [ 1 , 0 , 0 ] [1, 0, 0] [1,0,0]。
下标 4 4 4:字符为 ‘)’ \text{`)'} ‘)’,将 0 0 0 出栈,将 1 1 1 入栈,此时栈为 [ 1 , 0 , 1 ] [1, 0, 1] [1,0,1]。
下标 5 5 5:字符为 ‘(’ \text{`('} ‘(’,将 0 0 0 入栈,此时栈为 [ 1 , 0 , 1 , 0 ] [1, 0, 1, 0] [1,0,1,0]。
下标 6 6 6:字符为 ‘(’ \text{`('} ‘(’,将 0 0 0 入栈,此时栈为 [ 1 , 0 , 1 , 0 , 0 ] [1, 0, 1, 0, 0] [1,0,1,0,0]。
下标 7 7 7:字符为 ‘)’ \text{`)'} ‘)’,将 0 0 0 出栈,将 1 1 1 入栈,此时栈为 [ 1 , 0 , 1 , 0 , 1 ] [1, 0, 1, 0, 1] [1,0,1,0,1]。
下标 8 8 8:字符为 ‘)’ \text{`)'} ‘)’,将 1 1 1 出栈,然后将 0 0 0 出栈,将 2 2 2 入栈( 2 = 1 × 2 2 = 1 \times 2 2=1×2),此时栈为 [ 1 , 0 , 1 , 2 ] [1, 0, 1, 2] [1,0,1,2]。
下标 9 9 9:字符为 ‘)’ \text{`)'} ‘)’,将 2 2 2、 1 1 1 出栈,然后将 0 0 0 出栈,将 6 6 6 入栈( 6 = ( 2 + 1 ) × 2 6 = (2 + 1) \times 2 6=(2+1)×2),此时栈为 [ 1 , 6 ] [1, 6] [1,6]。
遍历结束,栈内的元素之和为 7 7 7,为字符串 s s s 的分数。
代码
class Solution {
public int scoreOfParentheses(String s) {
Deque<Integer> stack = new ArrayDeque<Integer>();
int length = s.length();
for (int i = 0; i < length; i++) {
if (s.charAt(i) == '(') {
stack.push(0);
} else {
int score = 0;
while (stack.peek() != 0) {
score += stack.pop();
}
stack.pop();
int newScore = score == 0 ? 1 : score * 2;
stack.push(newScore);
}
}
int totalScore = 0;
while (!stack.isEmpty()) {
totalScore += stack.pop();
}
return totalScore;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。需要遍历字符串 s s s 一次,每个下标处对应的分数入栈和出栈各一次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。空间复杂度主要取决于栈空间。
解法二
思路和算法
根据分数计算的规则, “()" \text{``()"} “()" 的分数是 1 1 1,字符串拼接的情况对每个原语分别计算分数,括号嵌套的情况根据嵌套层数计算分数。
对于字符串拼接的情况,只需要对每个原语分别计算分数即可,不需要特别的处理。
对于括号嵌套的情况,只有 “()" \text{``()"} “()"(连续的一对左括号和右括号)会对分数有贡献,其分数由所在层数决定。定义 “()" \text{``()"} “()" 的层数为其左边的未匹配左括号数量或者其右边的未匹配右括号数量,例如 “()(()(()))" \text{``()(()(()))"} “()(()(()))" 中有三对 “()" \text{``()"} “()",从左到右的每对 “()" \text{``()"} “()" 的深度依次为 0 0 0、 1 1 1、 2 2 2。当 “()" \text{``()"} “()" 所在的层数为 level \textit{level} level 时,其对分数的贡献为 2 level 2^\textit{level} 2level。
由此可以得到如下解法:将层数 level \textit{level} level 初始化为 0 0 0,从左到右遍历字符串 s s s,遇到 ‘(’ \text{`('} ‘(’ 则将 level \textit{level} level 加 1 1 1,遇到 ‘)’ \text{`)'} ‘)’ 则将 level \textit{level} level 减 1 1 1,当遇到 “()" \text{``()"} “()" 时,将 2 level 2^\textit{level} 2level 加到分数中,遍历结束之后即可得到字符串 s s s 的分数。
代码
class Solution {
public int scoreOfParentheses(String s) {
int score = 0, level = 0;
int length = s.length();
for (int i = 0; i < length; i++) {
if (s.charAt(i) == '(') {
level++;
} else {
level--;
if (s.charAt(i - 1) == '(') {
score += 1 << level;
}
}
}
return score;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。需要遍历字符串 s s s 一次,每个下标处的计算时间都是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( 1 ) O(1) O(1)。