Skip to content
← All posts

Minimum Number of Frogs Croaking: Tracking Concurrent Sequences

5 min read
leetcodeproblemmediumstrings

You are given a string croakOfFrogs that represents a combination of the string "croak" from different frogs. Multiple frogs can croak at the same time, so their sequences may be interleaved. Return the minimum number of different frogs needed to produce the given string, or -1 if the string is not a valid combination of "croak" sequences.

This is LeetCode 1419: Minimum Number of Frogs Croaking, a medium problem that tests your ability to simulate concurrent processes using counters.

The problem

Each frog must say the characters c, r, o, a, k in that exact order. Once a frog finishes saying "croak", it can start another "croak" or stay silent. The string is valid only if every character belongs to a complete, correctly ordered "croak" sequence.

For example, "crcoakroak" requires two frogs. The first frog starts with c and r, then the second frog begins its c. Their croaks overlap in the middle, but both complete the full c-r-o-a-k sequence.

input: "crcoakroak"c0r1c2o3a4k5r6o7a8k9frog 1: "croak"croakfrog 2: "croak"croak
Two frogs croak simultaneously. Their sequences interleave in the input string "crcoakroak". Green = frog 1, yellow = frog 2. Each frog produces one complete "croak" in order.

Counting frogs at each stage

The key insight is that you do not need to track individual frogs. Instead, you can track how many frogs are currently at each stage of saying "croak". When you see a c, a new frog starts (or a finished frog restarts). When you see an r, one of the frogs currently at c advances to r. When you see an o, one frog at r advances, and so on.

Think of five buckets labeled c, r, o, a, k. Each character in the input moves one frog from the previous bucket to the current bucket. The character c is special: it either pulls a frog from the k bucket (reuse) or creates a new frog.

If at any point a frog needs to advance but no frog is waiting at the previous stage, the string is invalid. For instance, seeing r when no frog is at c means the sequence is broken.

The minimum number of frogs is the maximum number of frogs that were active at any single moment. You can track this as the sum of the c, r, o, a counters (frogs that have started but not finished) at each step, and take the maximum.

Step-by-step walkthrough

Let's trace through "crcoakroak". Watch how the counters rise and fall as frogs progress through their sequences.

Step 1: Read 'c' at index 0. A new frog starts croaking.

active countersc:1r:0o:0a:0k:0max frogs1

Increment c counter. One frog is now in progress. Max frogs = 1.

Step 2: Read 'r' at index 1. The frog at 'c' moves to 'r'.

active countersc:0r:1o:0a:0k:0max frogs1

Decrement c, increment r. One frog is between 'r' and 'o'. Max frogs still 1.

Step 3: Read 'c' at index 2. A second frog starts.

active countersc:1r:1o:0a:0k:0max frogs2

Increment c. Now two frogs are active: one at 'r', one at 'c'. Max frogs = 2.

Step 4: Read 'o' at index 3. The frog at 'r' moves to 'o'.

active countersc:1r:0o:1a:0k:0max frogs2

Decrement r, increment o. Two frogs still active. Max frogs stays 2.

Step 5: Read 'a' at index 4. The frog at 'o' moves to 'a'.

active countersc:1r:0o:0a:1k:0max frogs2

Decrement o, increment a. Two frogs still active. Max frogs stays 2.

Step 6: Read 'k' at index 5. The frog at 'a' finishes croaking.

active countersc:1r:0o:0a:0k:0max frogs2

Decrement a, increment k, then k resets (frog done). One frog finished. One still at 'c'. Max = 2.

Step 7: Read 'r' at index 6. The remaining frog at 'c' moves to 'r'.

active countersc:0r:1o:0a:0k:0max frogs2

Decrement c, increment r. Only one frog active now. Max frogs stays 2.

Step 8: Read 'o' at index 7. The frog at 'r' moves to 'o'.

active countersc:0r:0o:1a:0k:0max frogs2

Decrement r, increment o. One frog active. Max stays 2.

Step 9: Read 'a' at index 8. The frog at 'o' moves to 'a'.

active countersc:0r:0o:0a:1k:0max frogs2

Decrement o, increment a. One frog nearing the end. Max stays 2.

Step 10: Read 'k' at index 9. The last frog finishes.

active countersc:0r:0o:0a:0k:0max frogs2

All counters zero. Both frogs completed valid "croak" sequences. Answer: 2.

The code

def minNumberOfFrogs(croakOfFrogs):
    c = r = o = a = k = 0
    max_frogs = 0

    for ch in croakOfFrogs:
        if ch == 'c':
            c += 1
        elif ch == 'r':
            if c == 0:
                return -1
            c -= 1
            r += 1
        elif ch == 'o':
            if r == 0:
                return -1
            r -= 1
            o += 1
        elif ch == 'a':
            if o == 0:
                return -1
            o -= 1
            a += 1
        elif ch == 'k':
            if a == 0:
                return -1
            a -= 1
            k += 1
        else:
            return -1

        active = c + r + o + a
        if active > max_frogs:
            max_frogs = active

    if c != 0 or r != 0 or o != 0 or a != 0:
        return -1

    return max_frogs

For each character we read, we check that a frog exists at the previous stage. If not, the string is invalid and we return -1 immediately. We also track the variable k to count completed croaks, though the validity check at the end only needs to confirm that c, r, o, and a are all zero (meaning every frog finished its sequence).

The active count at each step is the sum of frogs that have started but not yet finished. The maximum value of active across all steps is the answer, because that is the peak number of concurrent croaks.

You could also track max_frogs by just watching the c counter. Every time a frog starts with c, the total number of currently active frogs increases. However, summing all four in-progress counters makes the logic clearer and generalizes to similar problems.

Complexity analysis

ApproachTimeSpace
Counter trackingO(n)O(1)

Time: O(n). We scan through the string once, performing O(1) work per character. The total time is proportional to the length of the input string.

Space: O(1). We use five integer counters and one variable for the running maximum. No additional data structures are needed regardless of input size.

The building blocks

Sequential state machine tracking

The core pattern here is modeling a multi-step process as a series of counters. Each counter represents one state in a pipeline, and characters in the input move items from one state to the next. This same pattern appears whenever you need to validate or simulate a sequence of ordered events happening concurrently.

The general template looks like this: maintain one counter per stage, increment the current stage, and decrement the previous stage. If the previous stage counter would go negative, the input is invalid.

Peak concurrency measurement

The second building block is tracking the maximum number of concurrent processes. Instead of maintaining a list of active items, you simply keep a running maximum of the sum of in-progress counters. This avoids the overhead of explicit tracking and gives you the answer in O(1) extra space.

This pattern appears in problems involving overlapping intervals, concurrent tasks, and resource allocation, anywhere you need to answer "what is the most things happening at once?"

Edge cases

Single complete croak. "croak" requires exactly one frog. All counters return to zero cleanly.

Invalid character order. "croaak" has two a characters in a row, but there is only one frog at o by that point. The second a finds no frog at o, so the function returns -1.

String length not divisible by 5. If len(croakOfFrogs) % 5 != 0, the string cannot possibly contain only complete "croak" sequences. While the counter logic will catch this naturally (some counters will be nonzero at the end), you could add an early check for efficiency.

Frog reuse. "croakcroak" requires only one frog. After the first frog finishes its k, it can start a new c. The counter for k effectively tracks how many frogs are available for reuse.

From understanding to recall

The counter-based approach to this problem is elegant, but the details matter. You need to remember the order of checks (decrement the previous stage before incrementing the current one), the validity condition (previous counter must be positive), and the final check (all in-progress counters must be zero).

These small details are exactly the kind of thing that slips away between practice sessions. You understand the approach now, but in a week, will you remember whether to check the previous stage counter or the current one? Will you remember to validate that all counters are zero at the end?

Spaced repetition solves this by resurfacing the problem at increasing intervals. Each time you solve it from memory, the pattern gets a little more automatic. After a few repetitions, the counter pipeline becomes second nature and you can write it without hesitation.

Related posts