AlgoMaster Logo

Count Binary Substrings

easyFrequency7 min readUpdated June 23, 2026

Understanding the Problem

We need to count substrings where all 0's are grouped together and all 1's are grouped together, and both groups have the same size. So "0011" counts (two 0's followed by two 1's), "01" counts, "10" counts, "1100" counts. But "0101" does not count because the 0's and 1's are interleaved, not grouped.

Valid substrings always sit at the boundary between a group of one character and a group of the other. A group of three 0's followed by a group of two 1's ("00011") contains two valid substrings spanning that boundary: "01" and "0011". The count equals the minimum of the two group sizes, min(3, 2) = 2, and the same holds at every boundary between adjacent groups.

So the problem reduces to: group consecutive identical characters, then for each pair of adjacent groups, add the minimum of their sizes to the answer.

Key Constraints:

  • 1 <= s.length <= 10^5: A string this long has roughly n^2 / 2 substrings, about 5 billion at the upper limit, so checking each one individually is too slow. The target is an O(n) solution.
  • s[i] is either '0' or '1': With only two characters, every run of identical characters is followed by a run of the other character, so the string splits into alternating groups.

Approach 1: Brute Force

Intuition

Check every possible substring and count the ones that qualify. A valid substring is a block of one character followed by an equally sized block of the other, so the check verifies that the substring looks like "000...111..." or "111...000..." with equal halves. Equal counts of 0's and 1's force an even length, so only even-length substrings need checking.

One detail matters: a failed length does not rule out longer lengths from the same start. At i = 0 in "0011", length 2 gives "00" (invalid) while length 4 gives "0011" (valid). The inner loop has to try every even length rather than stopping at the first failure.

Algorithm

  1. Initialize count = 0.
  2. For each starting index i from 0 to n - 1:
    • For each even length len with i + len <= n:
      • Check that the first half is all one character and the second half is all the other character.
      • If both checks pass, increment count.
  3. Return count.

Example Walkthrough

1Check every even-length substring of "0011"
0
0
i
1
0
2
1
3
1
1/8

Code

The validity check rescans characters whose pattern is already fixed by the runs in the string. A group of five 0's followed by a group of three 1's produces exactly min(5, 3) = 3 valid substrings at that boundary, and that number comes from the two group sizes alone. The next approach computes the group sizes in one pass and sums the minimums of adjacent pairs.

Approach 2: Group Counting

Intuition

A binary string is a sequence of alternating groups: a run of 0's, then a run of 1's, then 0's again, and so on. For example, "00110011" has groups of sizes [2, 2, 2, 2].

A valid substring spans exactly two adjacent groups. If one group has size a and the next has size b, that boundary produces min(a, b) valid substrings: lengths 2, 4, 6, ... up to 2 * min(a, b), each taking the same number of characters from both sides of the boundary.

Algorithm

  1. Walk through the string and compute the sizes of consecutive groups. Store them in an array.
  2. For each pair of adjacent groups, add min(groups[i], groups[i+1]) to the result.
  3. Return the result.

Example Walkthrough

1Start: scan string to build group sizes
0
0
i
1
0
2
1
3
1
4
0
5
0
6
1
7
1
1/9

Code

This runs in O(n) time but stores every group size, even though the summing loop only ever reads two adjacent entries at a time. Two variables, the previous group size and the current one, carry all the state the sum needs, which removes the array entirely.

Approach 3: Optimized Single Pass (Two Counters)

Intuition

We can compress Approach 2 into a single pass without storing all group sizes. Maintain two variables: prevGroupSize (the size of the previous group) and currGroupSize (the size of the group currently being built). Every time the character changes, the current group becomes the previous group and a new current group starts at size 1.

At each character, if prevGroupSize >= currGroupSize, one more valid substring ends at this position, so increment the count.

This per-character check produces the same sum as Approach 2. Consider a boundary between a group of size a and a following group of size b. As the new group grows, currGroupSize takes the values 1, 2, ..., b, and the check prevGroupSize >= currGroupSize passes exactly while currGroupSize <= a. That is min(a, b) increments for this boundary, the same contribution the groups array gave.

Algorithm

  1. Initialize prevGroupSize = 0, currGroupSize = 1, and count = 0.
  2. Iterate from index 1 to n - 1:
    • If s[i] == s[i - 1], the current group extends: increment currGroupSize.
    • Otherwise, a new group starts: set prevGroupSize = currGroupSize and reset currGroupSize = 1.
    • If prevGroupSize >= currGroupSize, increment count.
  3. Return count.

Example Walkthrough

1Init: prev=0, curr=1, count=0. Start at i=1
0
0
curr grp
1
0
i
2
1
3
1
4
0
5
0
6
1
7
1
1/9

Code