Coding Interview Patterns Module 28 – String patterns
String to integer (atoi) and run-length encoding
Parse a clamped 32-bit number from text typed by people, and decode run-length codes with counts of several digits: the problems and where direct code breaks.
What you will learn
- Parse the integer at the start of messy text and clamp it to the 32-bit range without ever overflowing
- Prove that the overflow check before each digit is exact at both ends of the range
- Compare what the parsers of Python, Java, C++, Go and JavaScript accept and reject
- Decode and encode run-length codes whose counts have several digits
- Rewrite a character array in place with its run-length code, and prove that the writer never passes the reader
Before you start
What you’ll learn · Before you start
What you will learn
- Parse the integer at the start of messy text and clamp it to the 32-bit range without ever overflowing
- Prove that the overflow check before each digit is exact at both ends of the range
- Compare what the parsers of Python, Java, C++, Go and JavaScript accept and reject
- Decode and encode run-length codes whose counts have several digits
- Rewrite a character array in place with its run-length code, and prove that the writer never passes the reader
Before you start
On this page
String to integer, often called atoi after the C function, means reading the number at the start of a piece of text:
skip leading spaces, accept one sign, read digits until the first other character, and clamp the value to the 32-bit
range instead of letting it overflow. Run-length encoding stores a string as runs, each a letter and how many times it
repeats, so aaabcc becomes a3bc2. Both are simulations whose difficulty lies in the edge cases: a number or a count
with many digits, a value past the limit, a sign with nothing after it. This lesson states both problems, shows where
the direct code breaks at the stated sizes, and what to notice before writing the loops.
Two problems on a meter’s data
Problem A: string to integer. A smart-meter app receives readings typed by people. Write parse_clamped(s):
skip leading spaces, read at most one + or -, then the ASCII digits that follow, and stop at the first other
character. Return the value clamped to −2³¹ … 2³¹ − 1, or 0 if no digit follows. Limits: 0 ≤ len(s) ≤ 10⁵.
" -0042 kWh"gives −42, and"+-12"gives 0, because the second sign is not a digit."9999999999"gives 2,147,483,647, and" + 7"gives 0: a space after the sign ends the number.
Problem B: run-length encoding. The meter’s sensors report status letters, sent run-length encoded: each run is
its letter followed by its length, which is left out when it is 1. Write rle_decode(code), and compress(chars),
which rewrites a list of letters in place with its encoding and returns the new length. Counts can have several
digits, and runs can be 10⁵ letters long.
"a3bc2"decodes to"aaabcc", and"x12"to twelve x’s.compress(list("aaabcc"))returns 5, the list startinga3bc2;compress(list("abc"))returns 3.
Problem A reads characters until the first non-digit; problem B reads a letter and then its count
Text description of the diagram
The diagram has two rows, one under the other.
- The start of the reading " -0042 kWh" is drawn as eight cells, each with a character and what the parser does with it. The two spaces are skipped. The minus sign is read as the sign. The digits 0, 0, 4 and 2 are read one at a time, and the value after each is 0, 0, 4 and 42. The space after the 2 stops the parser, so the rest, kWh, is never read. With the sign, the result is −42.
- The code a3bc2 is drawn as three runs: a3 decodes to aaa, b with no count decodes to a single b, and c2 decodes to cc. Together they give aaabcc.
The direct code, and the inputs that break it
The first idea for problem A reads all the digits into one variable and clamps at the end. A reading of 10⁵ digits
overflows any fixed-size integer long before the end, and a 64-bit variable silently wraps: 18446744073709551621,
which is 2⁶⁴ + 5, comes out as 5. Python’s integers never overflow, but int() refuses more than 4,300 digits by
default, the limit CPython sets on decimal conversion. For problem B, reading each digit as a separate count turns
x12 into one x.
# The direct versions of both problems, and the inputs that break them.
LOW, HIGH = -(2**31), 2**31 - 1
def wrap64(x):
"""What a signed 64-bit variable holds after x overflows it (Java's long, Go's int64)."""
x %= 2**64
return x - 2**64 if x >= 2**63 else x
def parse_then_clamp(s, wrap):
"""Read the number into one variable and clamp at the end; wrap=True imitates a 64-bit variable."""
i = 0
while i < len(s) and s[i] == " ":
i += 1
sign = -1 if i < len(s) and s[i] == "-" else 1
if i < len(s) and s[i] in "+-":
i += 1
value = 0
while i < len(s) and "0" <= s[i] <= "9":
value = value * 10 + int(s[i])
if wrap:
value = wrap64(value)
i += 1
return min(max(sign * value, LOW), HIGH)
for reading in [" -0042 kWh", "9999999999", "18446744073709551621 kWh"]:
print(f"{reading!r:28} 64-bit: {parse_then_clamp(reading, True):>11} exact: {parse_then_clamp(reading, False):>11}")
try:
int("7" * 5000)
except ValueError as e:
print(f"\nint() on 5,000 digits: ValueError: {str(e).split(';')[0]}")
def decode_one_digit(code):
"""Treats every digit as a whole count, so a count of 12 becomes a 1 and then a 2."""
out = []
for c in code:
if c.isdigit():
out.append(out[-1] * (int(c) - 1))
else:
out.append(c)
return "".join(out)
print(f"\none digit per count: 'a3bc2' -> {decode_one_digit('a3bc2')!r}, 'x12' -> {decode_one_digit('x12')!r}") Output
' -0042 kWh' 64-bit: -42 exact: -42 '9999999999' 64-bit: 2147483647 exact: 2147483647 '18446744073709551621 kWh' 64-bit: 5 exact: 2147483647 int() on 5,000 digits: ValueError: Exceeds the limit (4300 digits) for integer string conversion: value has 5000 digits one digit per count: 'a3bc2' -> 'aaabcc', 'x12' -> 'x'
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 direct_attempts.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
What to notice
In problem A, decide the clamp before each digit is added, with a comparison that never leaves the 32-bit range: then the remaining digits cannot matter. In problem B, a count is a number like A’s, accumulated digit by digit. And the encoding of a run is never longer than the run, so the encoded letters can be written over the input as it is read.
Parsing, step by step, with an exact overflow check Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Why the check is exact, and why a negative overflow returns MIN
- Edge cases to settle before coding
- Why Python stops at 4,300 digits
- The parser in five languages, next to each language’s own parser
- Run-length decoding with counts of several digits
- Compressing in place, and why the writer never passes the reader
- Five parser bugs and one compression bug, reproduced
- Variants
- Exercise: readings and runs
- Parse a meter reading, and decode a run-length code
- A practice set with sample answers
- Check yourself
- Interview questions
- Write the overflow check of a 32-bit string-to-integer parser that never holds a value outside the int range, and show it is exact at both ends, including −2147483648. Why do many solutions get the negative end wrong?
- A form sends quantities to three services: a Java service calls Integer.parseInt, a Python job calls int(), and a C++ importer calls atoi. Give inputs on which they disagree, explain each difference, and define one rule that all three should share.
- Compress a character array in place into runs of a letter and its count, the count left out when it is 1, and prove the writer never overwrites unread input. A new format writes the count even when it is 1: can that still be done in place, and how?
- Key takeaways
6 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 20 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
References
- std::atoi, std::atol, std::atoll (cppreference.com) (cppreference.com)
- std::stoi, std::stol, std::stoll (cppreference.com) (cppreference.com)
- Integer.parseInt(String) (Java SE 25 API) (Oracle)
- Character.digit(char, int) (Java SE 25 API) (Oracle)
- StringBuilder.repeat(int, int) (Java SE 25 API) (Oracle)
- Package strconv: ParseInt and Atoi (The Go Authors)
- int() (The Python Standard Library: built-in functions) (Python Software Foundation)
- Integer string conversion length limitation (The Python Standard Library: built-in types) (Python Software Foundation)
- str.isdigit() and str.isdecimal() (The Python Standard Library: built-in types) (Python Software Foundation)
- Number.parseInt() (JavaScript reference) (MDN Web Docs)
- The Java Language Specification, Java SE 25: 15.17.2 Division operator / (Oracle)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress