2315. Count Asterisks
Problem Description
You are given a string s
that contains vertical bars '|'
and asterisks '*'
characters.
The vertical bars in the string are grouped into pairs. Specifically:
- The 1st and 2nd
'|'
form the first pair - The 3rd and 4th
'|'
form the second pair - The 5th and 6th
'|'
form the third pair - And so on...
Your task is to count the total number of '*'
characters in the string, but exclude any '*'
that appears between a pair of vertical bars.
For example, if we have a string like "l|*e*et|c**o|*de|"
:
- The 1st and 2nd
'|'
form a pair, so the'*'
characters between them (*e*et
) should not be counted - The 3rd and 4th
'|'
form another pair, so the'*'
character between them (*de
) should not be counted - The
'*'
characters outside of these pairs (c**o
) should be counted
The problem guarantees that each '|'
will belong to exactly one pair, meaning the total number of '|'
characters in the string will always be even.
Return the count of '*'
characters that are not between any pair of vertical bars.
Intuition
The key insight is to recognize that we're alternating between two states as we traverse the string: either we're outside a pair of vertical bars (where asterisks should be counted) or we're inside a pair (where asterisks should be ignored).
Think of it like a toggle switch. We start in the "counting" state since we begin outside any pairs. Every time we encounter a '|'
, we flip the switch:
- First
'|'
: Switch OFF counting (entering a pair) - Second
'|'
: Switch ON counting (exiting a pair) - Third
'|'
: Switch OFF counting (entering another pair) - Fourth
'|'
: Switch ON counting (exiting that pair)
This pattern continues throughout the string.
We can implement this toggle behavior using a simple variable that tracks our current state. When we see an asterisk '*'
, we only count it if we're in the "counting" state. When we see a vertical bar '|'
, we flip our state.
The XOR operation (^= 1
) is perfect for this toggle behavior - it flips between 1 (counting enabled) and 0 (counting disabled) each time we encounter a '|'
. Alternatively, we could use a boolean flag or any other mechanism to track this binary state.
By maintaining this simple state as we scan through the string once, we can efficiently determine which asterisks to count and which to ignore, solving the problem in a single pass.
Solution Approach
We implement the toggle mechanism using a simulation approach with two variables:
ans
: Accumulates the count of asterisks that are outside pairs of vertical barsok
: A flag that indicates whether we should count asterisks (1 = count, 0 = don't count)
Here's how the algorithm works step by step:
Initialization:
- Set
ans = 0
to store our final count - Set
ok = 1
to indicate we start in "counting mode" (outside any pairs)
Main Loop:
Traverse each character c
in the string s
:
-
If
c == '*'
:- Add
ok
toans
(this adds 1 ifok = 1
, or 0 ifok = 0
) - This elegantly handles the conditional counting without an explicit if-statement
- Add
-
If
c == '|'
:- Toggle
ok
using XOR:ok ^= 1
- This flips
ok
between 1 and 0:- If
ok = 1
, thenok ^= 1
makesok = 0
(entering a pair) - If
ok = 0
, thenok ^= 1
makesok = 1
(exiting a pair)
- If
- Toggle
-
Any other character: Simply continue (no action needed)
Return Result:
After processing all characters, return ans
which contains the total count of asterisks outside the pairs.
The beauty of this solution is its simplicity - by using the XOR operation for toggling and directly adding ok
to ans
, we avoid complex conditional logic while maintaining O(n) time complexity and O(1) space complexity.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the string "*|*a*|b**|c*|*"
step by step:
Initial Setup:
ans = 0
(count of asterisks outside pairs)ok = 1
(we start in counting mode)
Processing each character:
-
'*'
at index 0:- Since
ok = 1
, we add 1 toans
ans = 1
- Since
-
'|'
at index 1:- Toggle:
ok ^= 1
→ok = 0
(entering first pair)
- Toggle:
-
'*'
at index 2:- Since
ok = 0
, we add 0 toans
ans = 1
(unchanged, this asterisk is inside a pair)
- Since
-
'a'
at index 3:- Not
'*'
or'|'
, continue
- Not
-
'*'
at index 4:- Since
ok = 0
, we add 0 toans
ans = 1
(unchanged, still inside the first pair)
- Since
-
'|'
at index 5:- Toggle:
ok ^= 1
→ok = 1
(exiting first pair)
- Toggle:
-
'b'
at index 6:- Not
'*'
or'|'
, continue
- Not
-
'*'
at index 7:- Since
ok = 1
, we add 1 toans
ans = 2
- Since
-
'*'
at index 8:- Since
ok = 1
, we add 1 toans
ans = 3
- Since
-
'|'
at index 9:- Toggle:
ok ^= 1
→ok = 0
(entering second pair)
- Toggle:
-
'c'
at index 10:- Not
'*'
or'|'
, continue
- Not
-
'*'
at index 11:- Since
ok = 0
, we add 0 toans
ans = 3
(unchanged, inside second pair)
- Since
-
'|'
at index 12:- Toggle:
ok ^= 1
→ok = 1
(exiting second pair)
- Toggle:
-
'*'
at index 13:- Since
ok = 1
, we add 1 toans
ans = 4
- Since
Final Result: ans = 4
The asterisks counted were at indices 0, 7, 8, and 13 (all outside the pairs). The asterisks at indices 2, 4, and 11 were ignored because they were inside pairs.
Solution Implementation
1class Solution:
2 def countAsterisks(self, s: str) -> int:
3 """
4 Count asterisks that are not between pairs of vertical bars.
5
6 Args:
7 s: Input string containing asterisks (*) and vertical bars (|)
8
9 Returns:
10 Number of asterisks outside of vertical bar pairs
11 """
12 # Initialize counter for asterisks and flag for counting state
13 asterisk_count = 0
14 is_counting_enabled = 1 # 1 means we're outside bars, 0 means inside
15
16 # Iterate through each character in the string
17 for char in s:
18 if char == "*":
19 # Add to count only if we're outside vertical bar pairs
20 asterisk_count += is_counting_enabled
21 elif char == "|":
22 # Toggle counting state when encountering a vertical bar
23 # XOR with 1 flips between 0 and 1
24 is_counting_enabled ^= 1
25
26 return asterisk_count
27
1class Solution {
2 public int countAsterisks(String s) {
3 int asteriskCount = 0;
4 // Track whether we are outside vertical bars (1 = outside, 0 = inside)
5 int isOutsideBars = 1;
6
7 // Iterate through each character in the string
8 for (int i = 0; i < s.length(); i++) {
9 char currentChar = s.charAt(i);
10
11 // Count asterisks only when we are outside vertical bars
12 if (currentChar == '*') {
13 asteriskCount += isOutsideBars;
14 }
15 // Toggle the state when encountering a vertical bar
16 else if (currentChar == '|') {
17 isOutsideBars ^= 1; // XOR with 1 to toggle between 0 and 1
18 }
19 }
20
21 return asteriskCount;
22 }
23}
24
1class Solution {
2public:
3 int countAsterisks(string s) {
4 int asteriskCount = 0; // Counter for asterisks outside of pairs
5 int outsidePairs = 1; // Flag: 1 if outside vertical bar pairs, 0 if inside
6
7 // Iterate through each character in the string
8 for (char& currentChar : s) {
9 if (currentChar == '*') {
10 // Count asterisk only if we're outside of vertical bar pairs
11 asteriskCount += outsidePairs;
12 } else if (currentChar == '|') {
13 // Toggle the flag when encountering a vertical bar
14 // XOR with 1 flips between 0 and 1
15 outsidePairs ^= 1;
16 }
17 }
18
19 return asteriskCount;
20 }
21};
22
1/**
2 * Counts the number of asterisks that are not between pairs of vertical bars
3 * @param s - The input string containing asterisks (*) and vertical bars (|)
4 * @returns The count of asterisks outside of vertical bar pairs
5 */
6function countAsterisks(s: string): number {
7 // Initialize counter for valid asterisks
8 let asteriskCount: number = 0;
9
10 // Flag to track if we're outside a pair of vertical bars (1 = outside, 0 = inside)
11 let isOutsideBars: number = 1;
12
13 // Iterate through each character in the string
14 for (const character of s) {
15 if (character === '*') {
16 // Only count asterisk if we're outside vertical bar pairs
17 asteriskCount += isOutsideBars;
18 } else if (character === '|') {
19 // Toggle the flag when encountering a vertical bar
20 // XOR with 1 flips between 0 and 1
21 isOutsideBars ^= 1;
22 }
23 }
24
25 return asteriskCount;
26}
27
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the string s
. This is because the algorithm iterates through each character in the string exactly once using a single for loop. Each operation inside the loop (checking character equality, incrementing counter, XOR operation) takes constant time O(1)
.
The space complexity is O(1)
. The algorithm only uses a fixed amount of extra space regardless of the input size. It maintains two integer variables (ans
and ok
) to track the count of asterisks and the state of whether we're inside or outside pipe-delimited sections. These variables require constant space that doesn't grow with the input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Pairing Logic
A common mistake is thinking that vertical bars need to be "matched" like parentheses, where you look for opening and closing pairs. This leads to unnecessarily complex solutions that try to find matching bars.
Incorrect Approach:
# Wrong: Trying to match specific pairs
def countAsterisks(s: str) -> int:
bars = []
for i, char in enumerate(s):
if char == '|':
bars.append(i)
# Then trying to pair bars[0] with bars[1], bars[2] with bars[3], etc.
# This overcomplicates the problem
Why it's wrong: The problem states bars are paired sequentially (1st with 2nd, 3rd with 4th), not based on nesting or matching logic.
2. Using Wrong Initial State
Starting with is_counting_enabled = 0
instead of 1
would give incorrect results because we begin counting from the start of the string (outside any pairs).
Incorrect:
is_counting_enabled = 0 # Wrong initial state
Correct:
is_counting_enabled = 1 # We start outside any pairs, so counting is enabled
3. Counting All Characters Between Bars
Some might accidentally count all characters (not just asterisks) between vertical bars or misinterpret what should be excluded.
Incorrect Implementation:
def countAsterisks(s: str) -> int:
count = 0
inside_pair = False
for char in s:
if char == '|':
inside_pair = not inside_pair
elif not inside_pair: # Wrong: counting all non-bar characters
count += 1
return count
4. Off-by-One Errors with Toggle Timing
A subtle but critical error is toggling the state after processing the character instead of before, which would incorrectly include/exclude asterisks at boundaries.
Incorrect Order:
for char in s: if char == "*": asterisk_count += is_counting_enabled elif char == "|": is_counting_enabled ^= 1 # Correct position # Wrong would be: for char in s: if char == "|": is_counting_enabled ^= 1 if char == "*": # This would use the wrong state for asterisks right after bars asterisk_count += is_counting_enabled
5. Not Handling Edge Cases
While the problem guarantees an even number of vertical bars, developers might write defensive code that breaks the elegant solution:
Overengineered:
# Unnecessary complexity that could introduce bugs if s.count('|') % 2 != 0: raise ValueError("Odd number of bars")
The simple toggle mechanism naturally handles all valid inputs without special cases.
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
Recommended Readings
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
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!