Facebook Pixel

2315. Count Asterisks

EasyString
Leetcode Link

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. ans: Accumulates the count of asterisks that are outside pairs of vertical bars
  2. ok: 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 to ans (this adds 1 if ok = 1, or 0 if ok = 0)
    • This elegantly handles the conditional counting without an explicit if-statement
  • If c == '|':

    • Toggle ok using XOR: ok ^= 1
    • This flips ok between 1 and 0:
      • If ok = 1, then ok ^= 1 makes ok = 0 (entering a pair)
      • If ok = 0, then ok ^= 1 makes ok = 1 (exiting a pair)
  • 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 Evaluator

Example 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:

  1. '*' at index 0:

    • Since ok = 1, we add 1 to ans
    • ans = 1
  2. '|' at index 1:

    • Toggle: ok ^= 1ok = 0 (entering first pair)
  3. '*' at index 2:

    • Since ok = 0, we add 0 to ans
    • ans = 1 (unchanged, this asterisk is inside a pair)
  4. 'a' at index 3:

    • Not '*' or '|', continue
  5. '*' at index 4:

    • Since ok = 0, we add 0 to ans
    • ans = 1 (unchanged, still inside the first pair)
  6. '|' at index 5:

    • Toggle: ok ^= 1ok = 1 (exiting first pair)
  7. 'b' at index 6:

    • Not '*' or '|', continue
  8. '*' at index 7:

    • Since ok = 1, we add 1 to ans
    • ans = 2
  9. '*' at index 8:

    • Since ok = 1, we add 1 to ans
    • ans = 3
  10. '|' at index 9:

    • Toggle: ok ^= 1ok = 0 (entering second pair)
  11. 'c' at index 10:

    • Not '*' or '|', continue
  12. '*' at index 11:

    • Since ok = 0, we add 0 to ans
    • ans = 3 (unchanged, inside second pair)
  13. '|' at index 12:

    • Toggle: ok ^= 1ok = 1 (exiting second pair)
  14. '*' at index 13:

    • Since ok = 1, we add 1 to ans
    • ans = 4

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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

The three-steps of Depth First Search are:

  1. Identify states;
  2. Draw the state-space tree;
  3. DFS on the state-space tree.

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More