Coding Interview Patterns Module 24 – Dynamic programming: foundations
Segmenting strings with a dictionary
Split a string without spaces, such as a hashtag, into dictionary words: decide whether it can be done, list every way, and see why the brute force explodes.
What you will learn
- Decide whether a string splits into dictionary words with a table over cut positions
- Limit the inner loop of the table to the length of the longest word
- Count the segmentations modulo 10^9 + 7 and keep feasibility apart from the count
- List every segmentation with memoised suffixes, after checking that the output is small
- Compare a set of words with a trie that stops at the first missing prefix
Before you start
What you’ll learn · Before you start
What you will learn
- Decide whether a string splits into dictionary words with a table over cut positions
- Limit the inner loop of the table to the length of the longest word
- Count the segmentations modulo 10^9 + 7 and keep feasibility apart from the count
- List every segmentation with memoised suffixes, after checking that the output is small
- Compare a set of words with a trie that stops at the first missing prefix
Before you start
On this page
Word segmentation splits a string written without spaces, such as a hashtag, into words from a dictionary. Two questions come with it: can the string be split at all, and in which ways? Both have a table over the cut positions between letters, where each position asks whether, or in how many ways, the rest of the string can be split. Listing every way is different, because the number of segmentations can grow exponentially with the length, so the listing must check its size before it builds anything. This lesson poses both questions on original hashtags.
Two problems: can a hashtag be split, and in which ways?
Problem A, can it be split? Given a string of lowercase letters and a dictionary, decide whether the string can
be cut into dictionary words, each word usable any number of times. Limits: 1 ≤ len(text) ≤ 10,000, up to 1,000
words of 1 to 20 letters.
Problem B, every segmentation. Return every way to cut the string into words, each as words separated by spaces. The inputs are promised to have at most 10,000 segmentations.
monsoonsalewith the words mon, monsoon, soon, so, on, sale, sal and ale can be split in 3 ways: mon so on sale, mon soon sale and monsoon sale.goldencoinwith gold, golden, coins and den cannot be split: coin is not a word, only coins is.
The cut positions of monsoonsale, joined by dictionary words
Text description of the diagram
Each box is a position between two letters of the hashtag monsoonsale, from position 0 before the first letter to position 11 after the last. Each arrow is a dictionary word that starts at one position and ends at another.
- From position 0, "mon" leads to position 3 and "monsoon" leads to position 7.
- From position 3, "so" leads to position 5 and "soon" leads to position 7.
- From position 5, "on" leads to position 7.
- From position 7, "sale" leads to position 11, the end, and "sal" leads to position 10.
- No word starts at position 10, so it is a dead end.
Every path from position 0 to position 11 is one way to split the hashtag: mon so on sale, mon soon sale and monsoon sale. Three different paths reach position 7, and each continues the same way, which is the repetition a table over positions counts only once.
The brute force, and why it fails
Try every dictionary word that the rest of the string starts with, and every way on from there:
def splits_brute(text, words):
"""Tries every dictionary word that the rest of the text starts with, and every way on from there."""
calls = 0
def split_from(i):
nonlocal calls
calls += 1
if i == len(text):
return [[]] # the empty rest has one segmentation: no more words
found = []
for word in words:
if text.startswith(word, i):
found += [[word] + rest for rest in split_from(i + len(word))]
return found
return split_from(0), calls
tags = [
("monsoonsale", ["mon", "monsoon", "soon", "so", "on", "sale", "sal", "ale"]),
("goldencoin", ["gold", "golden", "coins", "den"]),
]
for text, words in tags:
found, calls = splits_brute(text, words)
shown = "; ".join(" ".join(s) for s in found) or "no segmentation"
print(f"{text}: {shown} ({calls} calls)")
for n in (10, 15, 20):
found, calls = splits_brute("a" * n + "b", ["a", "aa", "aaa"])
print(f"{n} a's and a b, words a, aa, aaa: {len(found)} segmentations after {calls:,} calls") Output
monsoonsale: mon soon sale; mon so on sale; monsoon sale (12 calls) goldencoin: no segmentation (3 calls) 10 a's and a b, words a, aa, aaa: 0 segmentations after 600 calls 15 a's and a b, words a, aa, aaa: 0 segmentations after 12,640 calls 20 a's and a b, words a, aa, aaa: 0 segmentations after 266,079 calls
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 split_brute.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
The hashtags are quick, but a string of a’s ending in b, with the words a, aa and aaa, has no segmentation at all and still makes the search try every way to cut the a’s: 600 calls for 10 a’s, 12,640 for 15 and 266,079 for 20, about 21 times more for every five letters. At 10,000 letters that is hopeless.
What to notice
- Whether the rest can be split depends only on where it starts. In the diagram, three different paths reach position 7, and each continues the same way.
- A word ends at most 20 letters after it starts, so each position has at most 20 candidate words, whatever the length of the string.
- The output of Problem B can be exponential. Thirty a’s with the words a, aa and aaa have more than 50 million segmentations, so a listing has to know its size before it starts.
Can it be split: the table over cut positions Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Bound the inner loop by the longest word
- A trie: stop at the first missing prefix
- Counting the segmentations modulo 1,000,000,007
- Listing every segmentation, after a size check
- The code in five languages
- Five bugs and the smallest inputs that break them
- Exercise
- Count the ways to split a hashtag into words
- Check yourself
- A practice set with sample answers
- Interview questions
- Give the time and memory of the word-break table for a text of n letters and a dictionary whose longest word has L letters. Why is it safe to try only the last L cuts before each position, and what does one lookup really cost in Python?
- A function must return every segmentation of a text, but some inputs have billions of them. Design it so that it never runs out of memory, say what it returns when the output is too large, and give its cost when the output is small.
- A service splits millions of short hashtags a day against one dictionary of 100,000 words, many sharing prefixes. What do you build once, what do you keep per request, and how do you stop one request's memo from changing the answer of the next?
- Key takeaways
7 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 21 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
- Set types: set, frozenset (The Python Standard Library) (Python Software Foundation)
- Common sequence operations: slicing (The Python Standard Library) (Python Software Foundation)
- functools.cache (The Python Standard Library) (Python Software Foundation)
- NIST Dictionary of Algorithms and Data Structures: trie (National Institute of Standards and Technology (NIST))
- String.substring (Java SE 25 API) (Oracle)
- std::basic_string_view (cppreference.com) (cppreference.com)
- The Go Programming Language Specification: Slice expressions (The Go Authors)
- AddressSanitizer (Clang documentation) (The LLVM Project)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress