Count and Say
The Count and Say solution is a repeated state transition: start with "1", scan the current string into maximal consecutive runs, and emit each run as…

Count and Say
Return the nth string in the count-and-say sequence, where the first string is "1" and each subsequent string is the run-length encoding of the preceding string.
Constraints
- 1 <= n <= 30
Important details
- Run-length encoding represents each maximal consecutive group by its length followed by the repeated digit.
- The input n is positive.
Key topics
The Count and Say solution is a repeated state transition: start with "1", scan the current string into maximal consecutive runs, and emit each run as count + digit.
The sequence begins:
"1"
"11"
"21"
"1211"
"111221"
"312211"
For example, "1211" becomes:
- one
1→"11" - one
2→"12" - two
1s →"21"
Concatenating those fragments produces "111221".
The key interview move is to stop treating “say the digits” as vague string manipulation. Each next term is the run-length encoding of the previous term. Once that transition is explicit, the problem becomes a controlled scan with a small amount of state.
Turn the recurrence into a transition
The sequence is 1-indexed:
term 1 = "1"
term n = run-length encoding of term n - 1
The requested result is a string, not a numeric value. Even though every character is a digit, the sequence describes representations. Converting a term to an integer would discard the structure we need to scan.
There are two layers of state:
- Outer state: the complete current term.
- Inner state: the run currently being scanned inside that term.
Starting with "1", apply the same transformation exactly n - 1 times.
That count matters. If n == 1, the starting value is already the answer. Applying the transformation n times would return the next term instead.
A useful way to express the algorithm is:
current = "1"
repeat n - 1 times:
current = next_term(current)
return current
The real work is therefore concentrated in next_term. It must partition the input into maximal adjacent groups and describe those groups from left to right.
Recognize the scan, not a frequency count
The most tempting wrong approach is to count how often each digit appears in the entire string. That loses the information the sequence depends on: order and adjacency.
Consider:
"1211"
The digit 1 appears three times overall, but the string contains three runs:
"1" "2" "11"
The first 1 and the final 11 must be described separately because they are separated by a 2.
A frequency map would see “three 1s and one 2.” The sequence needs “one 1, one 2, two 1s.” Those are different outputs.
For each run, the scan has four obligations:
- Identify the run's digit.
- Advance until the digit changes or the string ends.
- Emit the run exactly once.
- Continue from the first unprocessed character.
Use two indices:
i: start of the current run.j: first position after that run.
The central invariant is:
Before processing the run starting at
i, every character beforeihas been consumed and encoded exactly once. The interval fromiup to—but not including—jis the current maximal run.
This invariant gives the loop a clear job. It must move j to the run boundary, emit j - i and current[i], then set i = j.
A scan that emits before the run ends can fragment a group. A scan that searches globally can merge nonadjacent equal digits. The boundary is part of the answer.
Build one-step run-length encoding
Define a helper conceptually:
next_term(current) -> encoded version of current
The two-index version follows directly from the obligations:
i = 0
while i < len(current):
digit = current[i]
j = i
while j < len(current) and current[j] == digit:
j += 1
count = j - i
append str(count) + digit
i = j
The inner loop advances j through one maximal run. Because i then jumps to j, no character is revisited by the outer loop.
Dry run: encoding "1211"
Start with:
current = "1211"
i = 0
First run
current[i] is 1.
Advance j while the digit remains 1:
i = 0
j = 1
The run is current[0:1], which contains one 1.
Emit:
str(1) + "1" = "11"
Set i = 1.
Second run
current[1] is 2.
The next character is 1, so the run ends immediately:
i = 1
j = 2
Emit:
str(1) + "2" = "12"
Set i = 2.
Third run
current[2] is 1.
The next character is also 1, so advance to the end:
i = 2
j = 4
The run length is 4 - 2 = 2.
Emit:
str(2) + "1" = "21"
Set i = 4. The scan is complete.
The fragments are:
"11", "12", "21"
Joining them gives:
"111221"
The count must be converted with str(count) before concatenation. The output is text all the way through; it is not an arithmetic expression.
Prove the scan covers every run
The implementation is short, but the correctness argument should be visible.
Termination
For every run, the inner loop advances j by at least one position because every run is nonempty. After emitting the run, the outer loop sets i = j, so i also moves forward.
The outer recurrence applies the transformation only n - 1 times. Since n is positive and bounded by 30, both loops terminate.
Coverage and non-overlap
At any point, all positions before i have already been encoded. The inner loop advances j until either:
jreaches the end of the string, orcurrent[j]differs from the run digit.
Therefore, the interval [i, j) is a maximal run. It is nonempty, and the next scan begins exactly at j.
The runs are adjacent, non-overlapping, and cover the entire input string. No character is skipped or processed twice.
Correct emission
The run digit is current[i].
The run length is:
j - i
So the emitted fragment:
str(j - i) + current[i]
matches the definition of run-length encoding.
Because fragments are appended in left-to-right order, their order matches the order of runs in the previous term.
Induction over sequence terms
The base term is "1", which is correct for term 1.
Assume current is term k. The scan partitions it into every maximal consecutive run and emits the correct count-plus-digit fragment for each run. Therefore, next_term(current) is term k + 1.
Applying the transition n - 1 times produces term n.
That is the important distinction between code that happens to match a few examples and code whose state transition is justified.
Write the Python solution around visible state
For a Count and Say Python solution, I prefer the explicit boundary scan over a compressed loop. The variables expose the obligations:
imarks the first unprocessed character.jfinds the end of the current run.digitstores the run's value.partsstores one output fragment per completed run.
def countAndSay(n: int) -> str:
current = "1"
def next_term(value: str) -> str:
parts = []
i = 0
while i < len(value):
digit = value[i]
j = i
while j < len(value) and value[j] == digit:
j += 1
parts.append(str(j - i) + digit)
i = j
return "".join(parts)
for _ in range(n - 1):
current = next_term(current)
return current
The outer loop starts from a known valid term and performs exactly the remaining transitions. The helper does one complete run-length encoding pass.
Building a list of fragments and joining it at the end makes the output construction explicit. Each fragment corresponds to one consumed run. It also avoids making repeated string concatenation the central operation inside the scan.
Keep every term as a string. The count is temporarily an integer because subtraction gives us a length, but the final fragment converts it back to text. The sequence is about representation, not numeric magnitude.
I would choose this iterative implementation in an interview even though a recursive version is possible. Iteration mirrors the recurrence directly, keeps the number of transformations visible, and avoids adding call-stack state to a problem whose natural state is already sequential.
Complexity, growth, and edge-case checks
Let L_k be the length of term k.
One transformation scans all L_k characters and constructs the next term. Its time complexity is:
O(L_k)
To generate term n, the total work is:
O(L_1 + L_2 + ... + L_n)
If L is the maximum generated term length, this is often summarized as:
O(nL)
The current term and the next term must coexist during a transformation. The fragment list also holds the next output pieces, whose total size is proportional to the next term. Thus the working space is:
O(L)
where L is the maximum generated length among the terms being held.
Do not assume that every run count fits in one character. The output format is str(count) + digit, so a count may contribute multiple characters. The algorithm already handles that correctly because it converts the full integer count to text.
For the stated range 1 <= n <= 30, repeated linear scans are the intended approach. Hashing, dynamic programming, and global frequency counting do not solve the actual bottleneck or preserve the required adjacency information.
Check these cases before trusting the implementation:
| Case | What it tests | Expected result |
|---|---|---|
n = 1 | Base case and zero transitions | "1" |
n = 2 | A single one-character run | "11" |
n = 4 | Adjacent runs with different digits | "1211" |
Input "1211" to next_term | Separate runs and a final repeated run | "111221" |
| A run at the end of the string | Correct final-run handling | The inner loop stops at len(value) |
Common failure modes are predictable:
- Performing
ntransformations: returns termn + 1. - Forgetting the final run: often caused by emitting only when the next digit changes, without a final boundary check.
- Using a frequency map: merges equal digits that are not adjacent.
- Emitting one character at a time: fragments a maximal run instead of describing it once.
- Appending an integer directly: mixes numeric and string representations instead of producing text.
- Parsing the whole term as an integer: treats a representation sequence as arithmetic data and can lose leading-structure assumptions.
The best debugging technique is simple: print the current term before each transition, then print the run fragments produced by the helper. If a result is wrong, you can see whether the defect is in the outer iteration count or in one local run boundary.
Reuse the pattern: summarize, then transform
Count and Say is a compact example of a broader state-tracking pattern:
When the next object is a deterministic description of the previous object, first derive the one-step transformation. Then identify the smallest local state that lets you emit each description exactly once.
Here, the local summary is not a global count. It is the current run boundary:
- which digit is being described,
- where the run started,
- where it ends,
- how many characters it contains.
The implementation check is equally reusable:
Every output fragment must correspond to one maximal, already-consumed input region. The scan must neither revisit nor skip a character.
In an interview, write one transition by hand before writing the full loop. Mark the state before and after each emitted fragment:
input region -> output fragment
"1" -> "11"
"2" -> "12"
"11" -> "21"
Then encode that verified transition and repeat it n - 1 times.
The durable lesson is not merely “use two pointers.” It is sharper: read a sequence definition as a state machine. Name the current state, derive one deterministic transition, state the invariant that makes the transition safe, and test the boundaries where state changes. Clear the noise. Find the run. Emit it once. Then move on.
References
Research updated Sep 7, 2026


