856. Score of Parentheses
Problem Description
You are given a balanced parentheses string s
consisting only of (
and )
characters. Your task is to calculate the score of this string based on specific scoring rules.
The scoring rules are:
- The basic unit
"()"
has a score of1
- When two balanced parentheses strings
A
andB
are placed side by side (concatenated), the total score isA + B
- When a balanced parentheses string
A
is wrapped by an outer pair of parentheses like(A)
, the score becomes2 * A
For example:
"()"
has score1
(base case)"(())"
has score2
(the inner()
has score 1, wrapped by outer parentheses gives2 * 1 = 2
)"()()"
has score2
(two()
side by side gives1 + 1 = 2
)"(()(()))"
has score6
(breaking it down: inner left()
contributes2
, inner right(())
contributes4
, giving2 + 4 = 6
)
The key insight is that every ()
pair contributes to the final score, and its contribution is multiplied by 2^d
where d
is the depth (number of outer parentheses surrounding it). The solution tracks the depth as it iterates through the string, incrementing for (
and decrementing for )
. When it encounters a ()
pair (detected when current character is )
and previous is (
), it adds 2^d
to the total score using bit shifting (1 << d
).
Intuition
The key observation is that only the innermost ()
pairs actually contribute to the score - everything else is just a multiplier. Think about it: (())
is really just ()
with an extra layer that doubles its value. Similarly, ((()))
is ()
with two extra layers that multiply its value by 4.
Let's trace through an example to see the pattern. Consider (()(()))
:
- The first
()
at positions 1-2 is at depth 1 (one unclosed(
before it), so it contributes2^1 = 2
- The second
()
at positions 4-5 is at depth 2 (two unclosed(
before it), so it contributes2^2 = 4
- Total score:
2 + 4 = 6
This matches what we'd get by manually applying the rules: (()(()))
= (() + (()))
= 2*1 + 2*2 = 6
.
The brilliant insight is that we don't need to parse the entire structure or build a tree. We just need to:
- Track our current depth (how many unmatched
(
we've seen) - Whenever we see a
()
pair (which happens when current char is)
and previous was(
), add2^depth
to our answer - The depth naturally accounts for all the outer parentheses that would multiply this basic unit
Why does 2^depth
work? Because each level of nesting doubles the score. If a ()
is wrapped by n
layers of parentheses, its contribution gets doubled n
times, giving us 1 * 2 * 2 * ... * 2
(n times) = 2^n
.
This transforms a potentially complex recursive problem into a simple linear scan with a depth counter - elegant and efficient!
Learn more about Stack patterns.
Solution Approach
The implementation uses a single-pass linear scan with a depth counter to calculate the score efficiently.
Algorithm Steps:
-
Initialize variables:
ans
to store the cumulative score (starts at 0)d
to track the current depth of parentheses (starts at 0)
-
Iterate through each character in the string along with its index:
- When encountering
'('
: Increment the depthd += 1
(we're going one level deeper) - When encountering
')'
:- First decrement the depth
d -= 1
(we're closing a parenthesis) - Check if the previous character was
'('
(forming a()
pair) - If yes, add
1 << d
(which equals2^d
) to the answer
- First decrement the depth
- When encountering
-
Return the accumulated score
Why the depth adjustment order matters:
- For
'('
: We increment depth before any calculation - For
')'
: We decrement depth first, then check for()
pairs
This ensures that when we find a ()
pair, the depth d
represents the number of complete outer parentheses surrounding it.
Example walkthrough with "(()(()))"
:
Index: 0 1 2 3 4 5 6 7 Char: ( ( ) ( ( ) ) ) Depth: 1 2 1 2 3 2 1 0 At index 2: ')' and s[1]='(' → add 1<<1 = 2 At index 5: ')' and s[4]='(' → add 1<<2 = 4 Final answer: 2 + 4 = 6
Data structures used:
- Only two integer variables for tracking state
- No additional data structures needed
Time and Space Complexity:
- Time:
O(n)
- single pass through the string - Space:
O(1)
- only using constant extra space
The bit shift operation 1 << d
is an efficient way to calculate 2^d
, making the solution both elegant and performant.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through the string "(()(()))"
step by step to see how the solution calculates a score of 6.
Initial state: ans = 0
, depth = 0
Step-by-step iteration:
Position 0: '(' - Increment depth: depth = 1 - No scoring action Position 1: '(' - Increment depth: depth = 2 - No scoring action Position 2: ')' - Decrement depth: depth = 1 - Previous char s[1] = '(' → Found "()" pair! - Add 2^1 = 2 to answer: ans = 2 Position 3: '(' - Increment depth: depth = 2 - No scoring action Position 4: '(' - Increment depth: depth = 3 - No scoring action Position 5: ')' - Decrement depth: depth = 2 - Previous char s[4] = '(' → Found "()" pair! - Add 2^2 = 4 to answer: ans = 6 Position 6: ')' - Decrement depth: depth = 1 - Previous char s[5] = ')' → Not a "()" pair - No scoring action Position 7: ')' - Decrement depth: depth = 0 - Previous char s[6] = ')' → Not a "()" pair - No scoring action
Final answer: 6
The key insight illustrated here: only the two ()
pairs at positions 1-2 and 4-5 contribute to the score. The first pair has 1 outer layer (depth=1), contributing 2^1=2. The second pair has 2 outer layers (depth=2), contributing 2^2=4. Everything else just provides the multiplying structure.
Solution Implementation
1class Solution:
2 def scoreOfParentheses(self, s: str) -> int:
3 # Initialize total score and current depth level
4 total_score = 0
5 depth = 0
6
7 # Iterate through each character with its index
8 for index, char in enumerate(s):
9 if char == '(':
10 # Opening parenthesis increases nesting depth
11 depth += 1
12 else: # char == ')'
13 # Closing parenthesis decreases nesting depth
14 depth -= 1
15
16 # Check if this closing parenthesis forms "()" pattern
17 if s[index - 1] == '(':
18 # A "()" at depth d contributes 2^d to the score
19 # Using bit shift: 1 << depth is equivalent to 2^depth
20 total_score += 1 << depth
21
22 return total_score
23
1class Solution {
2 public int scoreOfParentheses(String s) {
3 int score = 0; // Total score of the balanced parentheses string
4 int depth = 0; // Current nesting depth (number of unclosed '(' brackets)
5
6 // Iterate through each character in the string
7 for (int i = 0; i < s.length(); i++) {
8 if (s.charAt(i) == '(') {
9 // Opening bracket increases the depth
10 depth++;
11 } else { // s.charAt(i) == ')'
12 // Closing bracket decreases the depth
13 depth--;
14
15 // Check if this closing bracket forms a "()" pair
16 // This happens when the previous character is '('
17 if (s.charAt(i - 1) == '(') {
18 // Each "()" at depth d contributes 2^d to the total score
19 // Using bit shift: 1 << d equals 2^d
20 score += 1 << depth;
21 }
22 }
23 }
24
25 return score;
26 }
27}
28
1class Solution {
2public:
3 int scoreOfParentheses(string s) {
4 int totalScore = 0; // Accumulated score of all balanced parentheses
5 int depth = 0; // Current nesting depth (number of open parentheses)
6
7 for (int i = 0; i < s.size(); ++i) {
8 if (s[i] == '(') {
9 // Opening parenthesis increases the depth
10 ++depth;
11 } else { // s[i] == ')'
12 // Closing parenthesis decreases the depth
13 --depth;
14
15 // Check if this closing parenthesis forms "()" pattern
16 // If previous character was '(', we have a base case "()"
17 // which contributes 2^depth to the total score
18 if (s[i - 1] == '(') {
19 totalScore += (1 << depth); // Add 2^depth to score
20 }
21 // Note: If previous was ')', this is just closing an outer group
22 // and doesn't contribute additional score
23 }
24 }
25
26 return totalScore;
27 }
28};
29
1function scoreOfParentheses(s: string): number {
2 let totalScore: number = 0; // Accumulated score of all balanced parentheses
3 let depth: number = 0; // Current nesting depth (number of open parentheses)
4
5 for (let i = 0; i < s.length; i++) {
6 if (s[i] === '(') {
7 // Opening parenthesis increases the depth
8 depth++;
9 } else { // s[i] === ')'
10 // Closing parenthesis decreases the depth
11 depth--;
12
13 // Check if this closing parenthesis forms "()" pattern
14 // If previous character was '(', we have a base case "()"
15 // which contributes 2^depth to the total score
16 if (s[i - 1] === '(') {
17 totalScore += (1 << depth); // Add 2^depth to score using bit shift
18 }
19 // Note: If previous was ')', this is just closing an outer group
20 // and doesn't contribute additional score
21 }
22 }
23
24 return totalScore;
25}
26
Time and Space Complexity
Time Complexity: O(n)
where n
is the length of the string s
. The algorithm iterates through each character in the string exactly once using a single for loop with enumerate. Each operation inside the loop (comparison, increment/decrement, bit shift, and addition) takes constant time O(1)
. Therefore, the overall time complexity is linear.
Space Complexity: O(1)
. The algorithm uses only a constant amount of extra space regardless of the input size. The variables used are:
ans
: an integer to store the accumulated scored
: an integer to track the current depth of nested parenthesesi
andc
: loop variables from enumerate
No additional data structures like arrays, stacks, or recursive call stacks are used, making the space complexity constant.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Depth Update Timing
A common mistake is updating the depth at the wrong time relative to score calculation, leading to incorrect results.
Incorrect Implementation:
def scoreOfParentheses(self, s: str) -> int:
total_score = 0
depth = 0
for index, char in enumerate(s):
if char == '(':
depth += 1
else: # char == ')'
# WRONG: Checking for "()" before updating depth
if s[index - 1] == '(':
total_score += 1 << depth # depth is off by 1!
depth -= 1
return total_score
This calculates 2^(d+1)
instead of 2^d
for each ()
pair because the depth hasn't been decremented yet.
Solution: Always decrement depth BEFORE calculating the contribution of a ()
pair.
2. Index Out of Bounds Error
When checking s[index - 1]
, the code assumes index > 0
. While this works for valid balanced parentheses (which never start with )
), it's fragile.
Defensive Implementation:
def scoreOfParentheses(self, s: str) -> int:
total_score = 0
depth = 0
for index, char in enumerate(s):
if char == '(':
depth += 1
else: # char == ')'
depth -= 1
# Add boundary check for safety
if index > 0 and s[index - 1] == '(':
total_score += 1 << depth
return total_score
3. Using Power Operation Instead of Bit Shift
While 2**depth
and pow(2, depth)
work correctly, they're less efficient than bit shifting.
Less Efficient:
total_score += 2 ** depth # Works but slower
Better: Use 1 << depth
for optimal performance, especially with larger depths.
4. Misunderstanding the Depth Concept
Some might think depth should start at 1 when entering the first (
, but depth represents the number of COMPLETE outer parentheses surrounding a ()
pair.
Wrong Mental Model:
- Thinking depth = 1 inside first parenthesis
- This would make
"()"
contribute2^1 = 2
instead of2^0 = 1
Correct Understanding:
- Depth = 0 means no outer parentheses around the current
()
pair - Each complete outer layer adds 1 to the depth
Which of the following problems can be solved with backtracking (select multiple)
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!