[Medium] 38. Count and Say
The count-and-say sequence is a sequence of digit strings defined by the recursive formula:
countAndSay(1) = "1"countAndSay(n)is the run-length encoding ofcountAndSay(n - 1)
Given a positive integer n, return the nth element of the count-and-say sequence.
Examples
Example 1:
Input: n = 4
Output: "1211"
Explanation:
countAndSay(1) = "1"
countAndSay(2) = "11" — one 1 → "11"
countAndSay(3) = "21" — two 1s → "21"
countAndSay(4) = "1211" — one 2, one 1 → "1211"
Example 2:
Input: n = 1
Output: "1"
Constraints
1 <= n <= 30
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Two pointers on string (this problem) | O(n) | O(1) | Palindrome, parsing |
| Hash map / frequency | O(n) | O(k) | Anagram, character counts |
| KMP / rolling hash | O(n) | O(n) | Pattern matching |
| Stack parsing | O(n) | O(n) | Decode string, parentheses |
Thinking Process
This is a pure simulation problem. Each term is generated by describing the previous term using run-length encoding:
- Start with
"1" - For each subsequent term, scan the current string and group consecutive identical characters
- For each group, output
(count)(digit)
Walk-Through
n=1: "1"
n=2: one 1 → "11"
n=3: two 1s → "21"
n=4: one 2, one 1 → "1211"
n=5: one 1, one 2, two 1s → "111221"
Two-Pointer Grouping
The key technique is using two pointers j and k to identify each run of identical characters:
jmarks the start of a groupkadvances whilecurr[k] == curr[j]- The run length is
k - j - After processing, jump
jforward tok
This is cleaner than maintaining a separate counter variable.
Approach: Iterative Simulation – O(n · L)
Build each term from the previous one, iterating from term 2 to term n. For each term, scan with two pointers to find consecutive groups.
class Solution:
def countAndSay(self, n):
curr = "1"
for _ in range(2, n + 1):
nxt = ""
i = 0
while i < len(curr):
j = i
while i < len(curr) and curr[i] == curr[j]:
i += 1
count = i - j
nxt += str(count) + curr[j]
curr = nxt
return currclass Solution:
def countAndSay(self, n):
curr = "1"
for _ in range(2, n + 1):
nxt = ""
i = 0
while i < len(curr):
j = i
while i < len(curr) and curr[i] == curr[j]:
i += 1
count = i - j
nxt += str(count) + curr[j]
curr = nxt
return curr
Solution Explanation
Approach: Two pointers on string (this problem)
Key idea: This is a pure simulation problem. Each term is generated by describing the previous term using run-length encoding:
How the code works:
- Start with
"1" - For each subsequent term, scan the current string and group consecutive identical characters
- For each group, output
(count)(digit)jmarks the start of a groupkadvances whilecurr[k] == curr[j]- The run length is
k - j
Walkthrough — input n = 4, expected output "1211":
countAndSay(1) = “1” countAndSay(2) = “11” — one 1 → “11” countAndSay(3) = “21” — two 1s → “21” countAndSay(4) = “1211” — one 2, one 1 → “1211”
Common Mistakes
- Off-by-one on the loop: starting from
i = 1instead ofi = 2(sincen = 1is the base case) - Forgetting to convert the count to a string (
to_string(k - j)) - Using a single index with a counter variable instead of the cleaner two-pointer approach
Key Takeaways
- Run-length encoding is the core operation – group consecutive identical characters and describe them
- The two-pointer grouping pattern (
j = kafter each group) is reusable in many string problems - This is a simulation problem – no clever trick needed, just clean implementation
Related Problems
- 443. String Compression – in-place run-length encoding
- 271. Encode and Decode Strings – string encoding design
References
- LC 38: Count and Say on LeetCode
- LeetCode Discuss — LC 38: Count and Say
- LeetCode Editorial (may require premium)