Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 16 – Backtracking

Partitioning strings with backtracking

Cut a string into valid pieces with backtracking: restore IPv4 addresses from digits, list palindromic partitions and count them without listing.

  • Intermediate
  • 35 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 7 of 35 minutes free
Free: Two problems about cutting strings · The brute force, and where it stops · What to notice. With a pass: the other 10 sections and 3 interview answers, about 28 minutes.

What you will learn

  • Split a string into valid pieces, such as the parts of an IPv4 address or palindromes
  • Eliminate a split as soon as a piece fails or the characters left cannot fill the pieces left
  • Build a palindrome table so that each check is O(1)
  • Count partitions with memoised suffixes when listing them is too slow
  • Validate the digits of an address against leading zeros, the 0 to 255 range and digits from other scripts

Before you start

What you’ll learn · Before you start

What you will learn

  • Split a string into valid pieces, such as the parts of an IPv4 address or palindromes
  • Eliminate a split as soon as a piece fails or the characters left cannot fill the pieces left
  • Build a palindrome table so that each check is O(1)
  • Count partitions with memoised suffixes when listing them is too slow
  • Validate the digits of an address against leading zeros, the 0 to 255 range and digits from other scripts

Before you start

On this page

Partitioning a string means cutting it into consecutive pieces so that every piece passes a test. Backtracking makes the cuts from left to right: choose the next piece, check it at once, solve the rest, and undo. Restoring IP addresses is the classic case: a string of digits is cut into four parts, each a number from 0 to 255 without a leading zero. In palindromic partitioning every piece must read the same backwards. A length bound stops a split when the characters left cannot fill the pieces left. A precomputed table answers each palindrome check in O(1). Memoised counts for each suffix give the number of partitions without listing them.

Two problems about cutting strings

Problem A: restore the addresses. A log lost the dots of its IPv4 addresses. Write restore_addresses(digits), which returns every dotted-decimal address that uses all the digits in order. Limits: up to 20 digits; fewer than 4 or more than 12 give none.

  • 172162541 gives 6 addresses, among them 172.16.254.1 and 17.216.25.41.
  • 10010 gives 1.0.0.10 and 10.0.1.0, and 0000 gives only 0.0.0.0.

Problem B: palindromic pieces. Write palindrome_partitions(word), which lists every way to cut a word into palindromes. Limits: up to 40 letters, with at most 100,000 partitions in the answer. noon gives n o o n, n oo n and noon; malayalam gives 8.

The digits 10010 split three ways: 1.0.0.10 and 10.0.1.0 are addresses, while 1.00.1.0 is not, because 00 has a leading zero.the digits 10010: three dots make four parts1.0.0.101.0.0.10, an address10.0.1.010.0.1.0, an address1.00.1.0no address: 00 has a leading zeroevery part: 1 to 3 digits, from 0 to 255, no leading zero

Three splits of 10010 into four parts

Text description of the diagram

The diagram shows the digits 10010 split into four parts by three dots, three times, one split per row.

  • The first row is 1, 0, 0 and 10, in solid boxes: the address 1.0.0.10.
  • The second row is 10, 0, 1 and 0, in solid boxes: the address 10.0.1.0.
  • The third row is 1, 00, 1 and 0, in dashed boxes: it is no address, because the part 00 has a leading zero.

A note at the bottom gives the rule for every part: 1 to 3 digits, a number from 0 to 255, and no leading zero.

The brute force, and where it stops

For A, try every placement of three dots and check the four parts. For B, try every set of cut positions and check every piece:

Every placement of dots, and every set of cuts, timed Python · brute/brute_force.py
"""Both problems by brute force: try every way to cut the string, then check every piece."""
import time
from itertools import combinations


def valid_part(part):
    """A part of a dotted-decimal IPv4 address: 1 to 3 digits, no leading zero, at most 255."""
    return 1 <= len(part) <= 3 and part.isdigit() and part.isascii() and (part == "0" or part[0] != "0") and int(part) <= 255


def addresses_brute(digits):
    """Every way to put three dots between the digits, kept when all four parts are valid."""
    found = []
    for a, b, c in combinations(range(1, len(digits)), 3):  # the three cut positions
        parts = [digits[:a], digits[a:b], digits[b:c], digits[c:]]
        if all(valid_part(p) for p in parts):
            found.append(".".join(parts))
    return found


def palindromes_brute(word):
    """Every set of cut positions, kept when every piece reads the same backwards."""
    n, found = len(word), []
    for mask in range(1 << max(n - 1, 0)):  # bit i: cut after letter i
        pieces, start = [], 0
        for i in range(n - 1):
            if mask >> i & 1:
                pieces.append(word[start : i + 1])
                start = i + 1
        pieces.append(word[start:])
        if all(p == p[::-1] for p in pieces):
            found.append(pieces)
    return found


print(addresses_brute("172162541"), addresses_brute("10010"), addresses_brute("0000"))
print(palindromes_brute("noon"))
print("letters  cut sets tried  palindromic partitions  time")
for n in (14, 16, 18):
    word = "abcdefghijklmnopqrstuvwxyz"[:n]
    start = time.perf_counter()
    found = palindromes_brute(word)
    seconds = time.perf_counter() - start
    print(f"{n:7} {1 << (n - 1):>15,} {len(found):>23}  {seconds:.2f} s")
print(f"40 letters: {1 << 39:,} cut sets to try")

Output

['17.216.25.41', '17.216.254.1', '172.16.25.41', '172.16.254.1', '172.162.5.41', '172.162.54.1'] ['1.0.0.10', '10.0.1.0'] ['0.0.0.0']
[['noon'], ['n', 'oo', 'n'], ['n', 'o', 'o', 'n']]
letters  cut sets tried  palindromic partitions  time
     14           8,192                       1  0.01 s
     16          32,768                       1  0.09 s
     18         131,072                       1  0.90 s
40 letters: 549,755,813,888 cut sets to try

This output changes from run to run: the times depend on the computer and on what else it is doing

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute_force.py

For A the brute force is fine: 12 digits allow only C(11, 3) = 165 placements, and a search over parts of 1 to 3 digits is at most four levels deep with three choices each, 121 calls at most. The difficulty is in the rules. For B each letter doubles the cut sets. Words of distinct letters have a single partition, yet 18 letters cost 131,072 cut sets, and 40 letters would cost 549,755,813,888.

What to notice

A bad piece ruins every split that contains it, so check each piece the moment it is cut. For addresses, the digits left must also fit the parts left, one to three digits each. For palindromes, every single letter passes, so every branch of the search reaches a full partition. The work then follows the size of the answer, and only the checks need to become faster.

Restoring addresses step by step, with a dry run Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Why every address is found exactly once
  • The bound and the rules, measured, in Python and JavaScript
    • Why the rules matter: the same digits, read differently
    • The code in Java, C++ and Go
    • Checked against every placement and against ipaddress
  • Palindromic partitions with a precomputed table
  • Counting partitions without listing them
  • Variants
  • Edge cases and common bugs
  • Exercise: every address hidden in a string of digits
    • Every address hidden in a string of digits
  • A practice set with answers
  • Check yourself
  • Interview questions
    • Restoring IPv4 addresses from a string of digits is a backtracking search. Why does it never take long, however long the string?
    • An address restorer passes its tests, but a colleague notices that it returns addresses with parts like 01 and accepts digits from other scripts. What exactly makes a part valid, why does each rule matter, and which tests would you add?
    • A service returns the palindromic partitions of words of up to 40 letters. For most words it answers at once, but for some it never finishes. Which words, why, and what would you change in the contract and in the code?
    • A palindromic partitioner checks each piece by comparing letters from both ends. A colleague suggests a precomputed table. When does the table pay for itself, what does it cost, and how would you show the difference?
  • Key takeaways

8 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.

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

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.