A clear explanation of generating the count-and-say sequence using run-length encoding.
Problem Restatement
We are given a positive integer n.
We need to return the nth term of the count-and-say sequence.
The sequence starts with:
countAndSay(1) = "1"For every n > 1, the next term is built by reading the previous term using run-length encoding.
Run-length encoding means grouping consecutive equal digits, then writing:
count + digitFor example:
"111221"has groups:
"111", "22", "1"So it becomes:
"31" + "22" + "11" = "312211"The constraint is:
1 <= n <= 30The problem statement defines the sequence recursively by countAndSay(1) = "1" and countAndSay(n) as the run-length encoding of countAndSay(n - 1).
Input and Output
| Item | Meaning |
|---|---|
| Input | A positive integer n |
| Output | The nth term as a string |
| Base case | countAndSay(1) = "1" |
| Transformation | Run-length encode the previous term |
| Constraint | 1 <= n <= 30 |
Function shape:
def countAndSay(n: int) -> str:
...Examples
Example 1:
n = 1The first term is defined as:
"1"Return:
"1"Example 2:
n = 4Build the sequence:
1: "1"
2: "11"
3: "21"
4: "1211"Explanation:
"1" -> one 1 -> "11"
"11" -> two 1s -> "21"
"21" -> one 2, one 1 -> "1211"Return:
"1211"Example 3:
n = 5The fourth term is:
"1211"Read it as:
one 1, one 2, two 1sSo the fifth term is:
"111221"First Thought: Simulate the Definition
The definition already tells us how to build the sequence.
Start from "1".
Then repeat n - 1 times:
- Read the current string from left to right.
- Group consecutive equal digits.
- For each group, append the group length and the digit.
- Replace the current string with the new string.
For example, from:
current = "3322251"The groups are:
| Group | Count | Output |
|---|---|---|
"33" | 2 | "23" |
"222" | 3 | "32" |
"5" | 1 | "15" |
"1" | 1 | "11" |
So:
"3322251" -> "23321511"Key Insight
Each step is just a scan over the previous string.
We do not need recursion, and we do not need to store every term.
We only keep the current term.
To encode one term, use a pointer i.
At position i, find how far the current run of equal digits goes.
For example:
current = "111221"
^
iThe digit is "1".
Move another pointer j until the digit changes.
"111221"
^^^
i jThe run has length 3, so append:
"31"Then continue from the next run.
Algorithm
Initialize:
current = "1"Repeat n - 1 times:
- Create an empty list
parts. - Set
i = 0. - While
i < len(current):- Set
j = i. - Move
jwhilecurrent[j] == current[i]. - The count is
j - i. - Append
str(count)andcurrent[i]. - Set
i = j.
- Set
- Join
partsinto the next string. - Assign it back to
current.
Return current.
Correctness
The sequence definition says that every term after the first is the run-length encoding of the previous term.
The algorithm starts with the correct first term, "1".
During one transformation, the inner loop partitions the current string into maximal consecutive groups of equal digits. For each group, it appends exactly the number of digits in that group followed by the digit itself. That is precisely the required run-length encoding.
After one iteration, current becomes the next term of the sequence. After n - 1 iterations, current has advanced from the first term to the nth term.
Therefore, the returned string is exactly countAndSay(n).
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(total generated length) | We scan each generated term once |
| Space | O(length of nth term) | We store the current term and the next term |
For this problem, n <= 30, so direct simulation is sufficient.
Implementation
class Solution:
def countAndSay(self, n: int) -> str:
current = "1"
for _ in range(n - 1):
parts = []
i = 0
while i < len(current):
j = i
while j < len(current) and current[j] == current[i]:
j += 1
count = j - i
parts.append(str(count))
parts.append(current[i])
i = j
current = "".join(parts)
return currentCode Explanation
Start from the first term:
current = "1"We need to transform it n - 1 times.
for _ in range(n - 1):Use a list instead of repeated string concatenation.
parts = []The pointer i marks the start of the current run.
i = 0The pointer j moves until the run ends.
while j < len(current) and current[j] == current[i]:
j += 1The run length is:
count = j - iAppend the count and the digit.
parts.append(str(count))
parts.append(current[i])Then move to the next run.
i = jAfter all runs are encoded, build the next term.
current = "".join(parts)Return the final term.
return currentTesting
def check(n: int, expected: str) -> None:
actual = Solution().countAndSay(n)
assert actual == expected, (n, actual, expected)
def run_tests():
check(1, "1")
check(2, "11")
check(3, "21")
check(4, "1211")
check(5, "111221")
check(6, "312211")
check(7, "13112221")
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
n = 1 | Base case |
n = 2 | First transformation |
n = 3 | Counts a repeated digit |
n = 4 | Counts two separate groups |
n = 5 | Counts a run of two 1s |
n = 6 | Counts multiple run lengths |
n = 7 | Confirms repeated simulation |