Chapter 20: Backtracking, Constraint Search and State Space
20.05Intermediate5 topics18 min5 interactive animations

String backtracking

Backtracking on strings: generating balanced parentheses, palindrome partitioning, phone-number letter combinations, restoring IP addresses and enumerating word-break sentences.

Learning goals

  • Build a string with StringBuilder and undo by deleting the last character
  • Cut a string into pieces that each satisfy a rule
  • Prune with early rules such as palindrome, range and dictionary tests

Prerequisites

The backtracking methodString fundamentals
20.05.1

Generate parentheses

List all balanced strings of n pairs of parentheses. Build them one character at a time with two counters, open and close. You may add ( while open < n, and ) while close < open. These two rules never create an invalid prefix, so every string of length 2n is balanced. There are Catalan(n) results: 1, 2, 5, 14 for n = 1 to 4.

The n = 3 preset finds 5 strings in a tree of 35 nodes; n = 4 finds 14 in 101 nodes. ✗ nodes are options rejected by the two rules.

Java
1static List<String> generateParentheses(int n) {
2 List<String> out = new ArrayList<>();
3 gen(0, 0, n, new StringBuilder(), out);
4 return out;
5}
6private static void gen(int open, int close, int n, StringBuilder sb, List<String> out) {
7 if (sb.length() == 2 * n) { out.add(sb.toString()); return; }
8 if (open < n) { sb.append('('); gen(open + 1, close, n, sb, out); sb.deleteCharAt(sb.length() - 1); }
9 if (close < open) { sb.append(')'); gen(open, close + 1, n, sb, out); sb.deleteCharAt(sb.length() - 1); }
10}

Generate valid parentheses

Interactive · edit the input

Your input

Try

partial solution

""

solutions found (0)

none yet
Setup

Build strings of 3 pairs one character at a time. Two validity rules prune the tree: never use more than 3 "(", and never write ")" unless an earlier "(" is still open. Every string that survives is balanced.

Step 1 / 84
Java
1static void gen(int open, int close, int n, StringBuilder sb, List<String> out) {
2 if (sb.length() == 2 * n) { out.add(sb.toString()); return; }
3 if (open < n) { // an opening parenthesis is still available
4 sb.append('('); // choose
5 gen(open + 1, close, n, sb, out); // explore
6 sb.deleteCharAt(sb.length() - 1); // unchoose
7 }
8 if (close < open) { // a closing one is valid only if something is open
9 sb.append(')');
10 gen(open, close + 1, n, sb, out);
11 sb.deleteCharAt(sb.length() - 1);
12 }
13}

Live state

depth

0

solutions

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 84 steps
StepLineStateWhat happens
1L1depth=0 solutions=0Build strings of 3 pairs one character at a time. Two validity rules prune the tree: never use more than 3 "(", and never write ")" unless an earlier "(" is still open. Every string that survives is balanced.
2L1depth=1 solutions=0Explore "".
3L4depth=1 solutions=0Choose (.
4L1depth=2 solutions=0Explore "(".
5L4depth=2 solutions=0Choose (.
6L1depth=3 solutions=0Explore "((".
7L4depth=3 solutions=0Choose (.
8L1depth=4 solutions=0Explore "(((".
9L3depth=5 solutions=0Reject (: all 3 opening parentheses are already used.
10L9depth=4 solutions=0Choose ).
11L1depth=5 solutions=0Explore "((()".
12L3depth=6 solutions=0Reject (: all 3 opening parentheses are already used.
13L9depth=5 solutions=0Choose ).
14L1depth=6 solutions=0Explore "((())".
15L3depth=7 solutions=0Reject (: all 3 opening parentheses are already used.
16L9depth=6 solutions=0Choose ).
17L1depth=7 solutions=0Explore "((()))".
18L2depth=7 solutions=1Complete and valid: record "((()))".
19L11depth=6 solutions=1Unchoose ): back to "((())".
20L11depth=5 solutions=1Unchoose ): back to "((()".
21L11depth=4 solutions=1Unchoose ): back to "(((".
22L6depth=3 solutions=1Unchoose (: back to "((".
23L9depth=3 solutions=1Choose ).
24L1depth=4 solutions=1Explore "(()".
25L4depth=4 solutions=1Choose (.
26L1depth=5 solutions=1Explore "(()(".
27L3depth=6 solutions=1Reject (: all 3 opening parentheses are already used.
28L9depth=5 solutions=1Choose ).
29L1depth=6 solutions=1Explore "(()()".
30L3depth=7 solutions=1Reject (: all 3 opening parentheses are already used.
31L9depth=6 solutions=1Choose ).
32L1depth=7 solutions=1Explore "(()())".
33L2depth=7 solutions=2Complete and valid: record "(()())".
34L11depth=6 solutions=2Unchoose ): back to "(()()".
35L11depth=5 solutions=2Unchoose ): back to "(()(".
36L6depth=4 solutions=2Unchoose (: back to "(()".
37L9depth=4 solutions=2Choose ).
38L1depth=5 solutions=2Explore "(())".
39L4depth=5 solutions=2Choose (.
40L1depth=6 solutions=2Explore "(())(".
41L3depth=7 solutions=2Reject (: all 3 opening parentheses are already used.
42L9depth=6 solutions=2Choose ).
43L1depth=7 solutions=2Explore "(())()".
44L2depth=7 solutions=3Complete and valid: record "(())()".
45L11depth=6 solutions=3Unchoose ): back to "(())(".
46L6depth=5 solutions=3Unchoose (: back to "(())".
47L8depth=6 solutions=3Reject ): there is nothing open to close.
48L11depth=4 solutions=3Unchoose ): back to "(()".
49L11depth=3 solutions=3Unchoose ): back to "((".
50L6depth=2 solutions=3Unchoose (: back to "(".
51L9depth=2 solutions=3Choose ).
52L1depth=3 solutions=3Explore "()".
53L4depth=3 solutions=3Choose (.
54L1depth=4 solutions=3Explore "()(".
55L4depth=4 solutions=3Choose (.
56L1depth=5 solutions=3Explore "()((".
57L3depth=6 solutions=3Reject (: all 3 opening parentheses are already used.
58L9depth=5 solutions=3Choose ).
59L1depth=6 solutions=3Explore "()(()".
60L3depth=7 solutions=3Reject (: all 3 opening parentheses are already used.
61L9depth=6 solutions=3Choose ).
62L1depth=7 solutions=3Explore "()(())".
63L2depth=7 solutions=4Complete and valid: record "()(())".
64L11depth=6 solutions=4Unchoose ): back to "()(()".
65L11depth=5 solutions=4Unchoose ): back to "()((".
66L6depth=4 solutions=4Unchoose (: back to "()(".
67L9depth=4 solutions=4Choose ).
68L1depth=5 solutions=4Explore "()()".
69L4depth=5 solutions=4Choose (.
70L1depth=6 solutions=4Explore "()()(".
71L3depth=7 solutions=4Reject (: all 3 opening parentheses are already used.
72L9depth=6 solutions=4Choose ).
73L1depth=7 solutions=4Explore "()()()".
74L2depth=7 solutions=5Complete and valid: record "()()()".
75L11depth=6 solutions=5Unchoose ): back to "()()(".
76L6depth=5 solutions=5Unchoose (: back to "()()".
77L8depth=6 solutions=5Reject ): there is nothing open to close.
78L11depth=4 solutions=5Unchoose ): back to "()(".
79L6depth=3 solutions=5Unchoose (: back to "()".
80L8depth=4 solutions=5Reject ): there is nothing open to close.
81L11depth=2 solutions=5Unchoose ): back to "(".
82L6depth=1 solutions=5Unchoose (: back to "".
83L8depth=2 solutions=5Reject ): there is nothing open to close.
84L13solutions=5 nodes=35 return=["((()))", "(()())", "(())()", "()(())", "()()()"]Search finished: 5 solutions, 34 choices tried. Backtracking explores every branch the pruning rules allow.
20.05.2

Palindrome partitioning

Cut a string into pieces so that every piece is a palindrome, and list all ways. At each position try every possible end of the next piece; if the piece is not a palindrome the branch is dead and is never explored. For "aab" the answers are [a, a, b] and [aa, b].

The animation marks each non-palindromic piece with ✗ and the reason. A palindrome test costs O(length); precomputing a table of palindromic substrings (Chapter 23) makes each test O(1).

Java
1static List<List<String>> partitionPalindromes(String s) {
2 List<List<String>> out = new ArrayList<>();
3 cut(s, 0, new ArrayList<>(), out);
4 return out;
5}
6private static void cut(String s, int start, List<String> path, List<List<String>> out) {
7 if (start == s.length()) { out.add(new ArrayList<>(path)); return; }
8 for (int end = start + 1; end <= s.length(); end++) {
9 if (!isPal(s, start, end - 1)) continue; // prune: only palindromic pieces
10 path.add(s.substring(start, end));
11 cut(s, end, path, out);
12 path.remove(path.size() - 1);
13 }
14}
15private static boolean isPal(String s, int i, int j) {
16 while (i < j) if (s.charAt(i++) != s.charAt(j--)) return false;
17 return true;
18}

Palindrome partitioning

Interactive · edit the input

Your input

Try

partial solution

[] then "aab"

solutions found (0)

none yet
Setup

Cut "aab" into pieces that are all palindromes. At each position try every next piece; a piece that is not a palindrome is rejected at once, so its whole subtree is never built.

Step 1 / 22
Java
1static void partition(String s, int start, List<String> path, List<List<String>> out) {
2 if (start == s.length()) { out.add(new ArrayList<>(path)); return; }
3 for (int end = start + 1; end <= s.length(); end++) {
4 if (!isPalindrome(s, start, end - 1)) continue; // prune: only palindromic pieces
5 path.add(s.substring(start, end)); // choose
6 partition(s, end, path, out); // explore
7 path.remove(path.size() - 1); // unchoose
8 }
9}

Live state

depth

0

solutions

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 22 steps
StepLineStateWhat happens
1L1depth=0 solutions=0Cut "aab" into pieces that are all palindromes. At each position try every next piece; a piece that is not a palindrome is rejected at once, so its whole subtree is never built.
2L1depth=1 solutions=0Explore [] then "aab".
3L5depth=1 solutions=0Choose "a".
4L1depth=2 solutions=0Explore ["a"] then "ab".
5L5depth=2 solutions=0Choose "a".
6L1depth=3 solutions=0Explore ["a", "a"] then "b".
7L5depth=3 solutions=0Choose "b".
8L1depth=4 solutions=0Explore ["a", "a", "b"] then "".
9L2depth=4 solutions=1Complete and valid: record [a, a, b].
10L7depth=3 solutions=1Unchoose "b": back to ["a", "a"] then "b".
11L7depth=2 solutions=1Unchoose "a": back to ["a"] then "ab".
12L4depth=3 solutions=1Reject "ab": "ab" is not a palindrome.
13L7depth=1 solutions=1Unchoose "a": back to [] then "aab".
14L5depth=1 solutions=1Choose "aa".
15L1depth=2 solutions=1Explore ["aa"] then "b".
16L5depth=2 solutions=1Choose "b".
17L1depth=3 solutions=1Explore ["aa", "b"] then "".
18L2depth=3 solutions=2Complete and valid: record [aa, b].
19L7depth=2 solutions=2Unchoose "b": back to ["aa"] then "b".
20L7depth=1 solutions=2Unchoose "aa": back to [] then "aab".
21L4depth=2 solutions=2Reject "aab": "aab" is not a palindrome.
22L9solutions=2 nodes=8 return=[[a, a, b], [aa, b]]Search finished: 2 solutions, 7 choices tried. Backtracking explores every branch the pruning rules allow.
20.05.3

Letter combinations

Each digit 2-9 on a phone keypad stands for 3 or 4 letters. List every string a digit sequence can spell. At position i the options are the letters of digit i; there is no invalid option, so nothing is pruned and the tree has exactly one leaf per answer. The number of results is the product of the letter counts: for "23" it is 3 × 3 = 9.

This is the simplest backtracking shape, the Cartesian product, and a good template for any problem where each position has its own independent list of choices.

Java
1static final String[] LETTERS = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
2
3static List<String> letterCombinations(String digits) {
4 List<String> out = new ArrayList<>();
5 if (digits.isEmpty()) return out;
6 spell(digits, 0, new StringBuilder(), out);
7 return out;
8}
9private static void spell(String digits, int i, StringBuilder sb, List<String> out) {
10 if (i == digits.length()) { out.add(sb.toString()); return; }
11 for (char c : LETTERS[digits.charAt(i) - '0'].toCharArray()) {
12 sb.append(c);
13 spell(digits, i + 1, sb, out);
14 sb.deleteCharAt(sb.length() - 1);
15 }
16}

Letter combinations of a phone number

Interactive · edit the input

Your input

Try

partial solution

""

solutions found (0)

none yet
Setup

Each digit offers its letters; a combination takes one letter per digit. There is nothing to prune: the tree has 9 leaves, one per combination.

Step 1 / 48
Java
1static final String[] LETTERS = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
2static void combos(String digits, int i, StringBuilder sb, List<String> out) {
3 if (i == digits.length()) { out.add(sb.toString()); return; }
4 for (char c : LETTERS[digits.charAt(i) - '0'].toCharArray()) {
5 sb.append(c); // choose
6 combos(digits, i + 1, sb, out); // explore
7 sb.deleteCharAt(sb.length() - 1); // unchoose
8 }
9}

Live state

depth

0

solutions

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 48 steps
StepLineStateWhat happens
1L2depth=0 solutions=0Each digit offers its letters; a combination takes one letter per digit. There is nothing to prune: the tree has 9 leaves, one per combination.
2L2depth=1 solutions=0Explore "".
3L5depth=1 solutions=0Choose a.
4L2depth=2 solutions=0Explore "a".
5L5depth=2 solutions=0Choose d.
6L2depth=3 solutions=0Explore "ad".
7L3depth=3 solutions=1Complete and valid: record "ad".
8L7depth=2 solutions=1Unchoose d: back to "a".
9L5depth=2 solutions=1Choose e.
10L2depth=3 solutions=1Explore "ae".
11L3depth=3 solutions=2Complete and valid: record "ae".
12L7depth=2 solutions=2Unchoose e: back to "a".
13L5depth=2 solutions=2Choose f.
14L2depth=3 solutions=2Explore "af".
15L3depth=3 solutions=3Complete and valid: record "af".
16L7depth=2 solutions=3Unchoose f: back to "a".
17L7depth=1 solutions=3Unchoose a: back to "".
18L5depth=1 solutions=3Choose b.
19L2depth=2 solutions=3Explore "b".
20L5depth=2 solutions=3Choose d.
21L2depth=3 solutions=3Explore "bd".
22L3depth=3 solutions=4Complete and valid: record "bd".
23L7depth=2 solutions=4Unchoose d: back to "b".
24L5depth=2 solutions=4Choose e.
25L2depth=3 solutions=4Explore "be".
26L3depth=3 solutions=5Complete and valid: record "be".
27L7depth=2 solutions=5Unchoose e: back to "b".
28L5depth=2 solutions=5Choose f.
29L2depth=3 solutions=5Explore "bf".
30L3depth=3 solutions=6Complete and valid: record "bf".
31L7depth=2 solutions=6Unchoose f: back to "b".
32L7depth=1 solutions=6Unchoose b: back to "".
33L5depth=1 solutions=6Choose c.
34L2depth=2 solutions=6Explore "c".
35L5depth=2 solutions=6Choose d.
36L2depth=3 solutions=6Explore "cd".
37L3depth=3 solutions=7Complete and valid: record "cd".
38L7depth=2 solutions=7Unchoose d: back to "c".
39L5depth=2 solutions=7Choose e.
40L2depth=3 solutions=7Explore "ce".
41L3depth=3 solutions=8Complete and valid: record "ce".
42L7depth=2 solutions=8Unchoose e: back to "c".
43L5depth=2 solutions=8Choose f.
44L2depth=3 solutions=8Explore "cf".
45L3depth=3 solutions=9Complete and valid: record "cf".
46L7depth=2 solutions=9Unchoose f: back to "c".
47L7depth=1 solutions=9Unchoose c: back to "".
48L9solutions=9 nodes=13 return=["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]Search finished: 9 solutions, 12 choices tried. Backtracking explores every branch the pruning rules allow.
20.05.4

Restore IP addresses

Insert three dots into a digit string to make a valid IPv4 address: four numbers from 0 to 255 without leading zeros. Choose segment lengths 1, 2 or 3 at each step; reject a segment with a leading zero (except "0" itself) or a value above 255; and stop when four segments are chosen, accepting only if every digit was used.

For "25525511" the search tries up to 3 digits at each of 4 places and finds 7 valid addresses, among them 25.52.55.11 and 255.255.1.1. The tree stays tiny (69 nodes here) because lengths are capped at 3 and invalid segments are rejected immediately.

Java
1static List<String> restoreIp(String s) {
2 List<String> out = new ArrayList<>();
3 segment(s, 0, new ArrayList<>(), out);
4 return out;
5}
6private static void segment(String s, int start, List<String> parts, List<String> out) {
7 if (parts.size() == 4) {
8 if (start == s.length()) out.add(String.join(".", parts)); // used every digit
9 return;
10 }
11 for (int len = 1; len <= 3 && start + len <= s.length(); len++) {
12 String seg = s.substring(start, start + len);
13 if ((seg.length() > 1 && seg.charAt(0) == '0') || Integer.parseInt(seg) > 255) continue; // invalid segment
14 parts.add(seg);
15 segment(s, start + len, parts, out);
16 parts.remove(parts.size() - 1);
17 }
18}

Restore IP addresses

Interactive · edit the input

Your input

Try

partial solution

(nothing yet) · 8 digits left

solutions found (0)

none yet
Setup

Cut "25525511" into exactly four numbers from 0 to 255 without leading zeros, using every digit. A segment has 1 to 3 digits; invalid segments are rejected immediately.

Step 1 / 210
Java
1static void restore(String s, int start, List<String> parts, List<String> out) {
2 if (parts.size() == 4) {
3 if (start == s.length()) out.add(String.join(".", parts)); // used every digit
4 return;
5 }
6 for (int len = 1; len <= 3 && start + len <= s.length(); len++) {
7 String seg = s.substring(start, start + len);
8 if ((seg.length() > 1 && seg.charAt(0) == '0') || Integer.parseInt(seg) > 255) continue; // invalid segment
9 parts.add(seg); // choose
10 restore(s, start + len, parts, out); // explore
11 parts.remove(parts.size() - 1); // unchoose
12 }
13}

Live state

depth

0

solutions

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 210 steps
StepLineStateWhat happens
1L1depth=0 solutions=0Cut "25525511" into exactly four numbers from 0 to 255 without leading zeros, using every digit. A segment has 1 to 3 digits; invalid segments are rejected immediately.
2L1depth=1 solutions=0Explore (nothing yet) · 8 digits left.
3L9depth=1 solutions=0Choose "2".
4L1depth=2 solutions=0Explore 2 · 7 digits left.
5L9depth=2 solutions=0Choose "5".
6L1depth=3 solutions=0Explore 2.5 · 6 digits left.
7L9depth=3 solutions=0Choose "5".
8L1depth=4 solutions=0Explore 2.5.5 · 5 digits left.
9L9depth=4 solutions=0Choose "2".
10L1depth=5 solutions=0Explore 2.5.5.2 · 4 digits left.
11L4depth=5 solutions=0Complete, but not a solution: dead end.
12L11depth=4 solutions=0Unchoose "2": back to 2.5.5 · 5 digits left.
13L9depth=4 solutions=0Choose "25".
14L1depth=5 solutions=0Explore 2.5.5.25 · 3 digits left.
15L4depth=5 solutions=0Complete, but not a solution: dead end.
16L11depth=4 solutions=0Unchoose "25": back to 2.5.5 · 5 digits left.
17L9depth=4 solutions=0Choose "255".
18L1depth=5 solutions=0Explore 2.5.5.255 · 2 digits left.
19L4depth=5 solutions=0Complete, but not a solution: dead end.
20L11depth=4 solutions=0Unchoose "255": back to 2.5.5 · 5 digits left.
21L11depth=3 solutions=0Unchoose "5": back to 2.5 · 6 digits left.
22L9depth=3 solutions=0Choose "52".
23L1depth=4 solutions=0Explore 2.5.52 · 4 digits left.
24L9depth=4 solutions=0Choose "5".
25L1depth=5 solutions=0Explore 2.5.52.5 · 3 digits left.
26L4depth=5 solutions=0Complete, but not a solution: dead end.
27L11depth=4 solutions=0Unchoose "5": back to 2.5.52 · 4 digits left.
28L9depth=4 solutions=0Choose "55".
29L1depth=5 solutions=0Explore 2.5.52.55 · 2 digits left.
30L4depth=5 solutions=0Complete, but not a solution: dead end.
31L11depth=4 solutions=0Unchoose "55": back to 2.5.52 · 4 digits left.
32L8depth=5 solutions=0Reject "551": 551 is above 255.
33L11depth=3 solutions=0Unchoose "52": back to 2.5 · 6 digits left.
34L8depth=4 solutions=0Reject "525": 525 is above 255.
35L11depth=2 solutions=0Unchoose "5": back to 2 · 7 digits left.
36L9depth=2 solutions=0Choose "55".
37L1depth=3 solutions=0Explore 2.55 · 5 digits left.
38L9depth=3 solutions=0Choose "2".
39L1depth=4 solutions=0Explore 2.55.2 · 4 digits left.
40L9depth=4 solutions=0Choose "5".
41L1depth=5 solutions=0Explore 2.55.2.5 · 3 digits left.
42L4depth=5 solutions=0Complete, but not a solution: dead end.
43L11depth=4 solutions=0Unchoose "5": back to 2.55.2 · 4 digits left.
44L9depth=4 solutions=0Choose "55".
45L1depth=5 solutions=0Explore 2.55.2.55 · 2 digits left.
46L4depth=5 solutions=0Complete, but not a solution: dead end.
47L11depth=4 solutions=0Unchoose "55": back to 2.55.2 · 4 digits left.
48L8depth=5 solutions=0Reject "551": 551 is above 255.
49L11depth=3 solutions=0Unchoose "2": back to 2.55 · 5 digits left.
50L9depth=3 solutions=0Choose "25".
51L1depth=4 solutions=0Explore 2.55.25 · 3 digits left.
52L9depth=4 solutions=0Choose "5".
53L1depth=5 solutions=0Explore 2.55.25.5 · 2 digits left.
54L4depth=5 solutions=0Complete, but not a solution: dead end.
55L11depth=4 solutions=0Unchoose "5": back to 2.55.25 · 3 digits left.
56L9depth=4 solutions=0Choose "51".
57L1depth=5 solutions=0Explore 2.55.25.51 · 1 digit left.
58L4depth=5 solutions=0Complete, but not a solution: dead end.
59L11depth=4 solutions=0Unchoose "51": back to 2.55.25 · 3 digits left.
60L8depth=5 solutions=0Reject "511": 511 is above 255.
61L11depth=3 solutions=0Unchoose "25": back to 2.55 · 5 digits left.
62L9depth=3 solutions=0Choose "255".
63L1depth=4 solutions=0Explore 2.55.255 · 2 digits left.
64L9depth=4 solutions=0Choose "1".
65L1depth=5 solutions=0Explore 2.55.255.1 · 1 digit left.
66L4depth=5 solutions=0Complete, but not a solution: dead end.
67L11depth=4 solutions=0Unchoose "1": back to 2.55.255 · 2 digits left.
68L9depth=4 solutions=0Choose "11".
69L1depth=5 solutions=0Explore 2.55.255.11 · 0 digits left.
70L3depth=5 solutions=1Complete and valid: record 2.55.255.11.
71L11depth=4 solutions=1Unchoose "11": back to 2.55.255 · 2 digits left.
72L11depth=3 solutions=1Unchoose "255": back to 2.55 · 5 digits left.
73L11depth=2 solutions=1Unchoose "55": back to 2 · 7 digits left.
74L8depth=3 solutions=1Reject "552": 552 is above 255.
75L11depth=1 solutions=1Unchoose "2": back to (nothing yet) · 8 digits left.
76L9depth=1 solutions=1Choose "25".
77L1depth=2 solutions=1Explore 25 · 6 digits left.
78L9depth=2 solutions=1Choose "5".
79L1depth=3 solutions=1Explore 25.5 · 5 digits left.
80L9depth=3 solutions=1Choose "2".
81L1depth=4 solutions=1Explore 25.5.2 · 4 digits left.
82L9depth=4 solutions=1Choose "5".
83L1depth=5 solutions=1Explore 25.5.2.5 · 3 digits left.
84L4depth=5 solutions=1Complete, but not a solution: dead end.
85L11depth=4 solutions=1Unchoose "5": back to 25.5.2 · 4 digits left.
86L9depth=4 solutions=1Choose "55".
87L1depth=5 solutions=1Explore 25.5.2.55 · 2 digits left.
88L4depth=5 solutions=1Complete, but not a solution: dead end.
89L11depth=4 solutions=1Unchoose "55": back to 25.5.2 · 4 digits left.
90L8depth=5 solutions=1Reject "551": 551 is above 255.
91L11depth=3 solutions=1Unchoose "2": back to 25.5 · 5 digits left.
92L9depth=3 solutions=1Choose "25".
93L1depth=4 solutions=1Explore 25.5.25 · 3 digits left.
94L9depth=4 solutions=1Choose "5".
95L1depth=5 solutions=1Explore 25.5.25.5 · 2 digits left.
96L4depth=5 solutions=1Complete, but not a solution: dead end.
97L11depth=4 solutions=1Unchoose "5": back to 25.5.25 · 3 digits left.
98L9depth=4 solutions=1Choose "51".
99L1depth=5 solutions=1Explore 25.5.25.51 · 1 digit left.
100L4depth=5 solutions=1Complete, but not a solution: dead end.
101L11depth=4 solutions=1Unchoose "51": back to 25.5.25 · 3 digits left.
102L8depth=5 solutions=1Reject "511": 511 is above 255.
103L11depth=3 solutions=1Unchoose "25": back to 25.5 · 5 digits left.
104L9depth=3 solutions=1Choose "255".
105L1depth=4 solutions=1Explore 25.5.255 · 2 digits left.
106L9depth=4 solutions=1Choose "1".
107L1depth=5 solutions=1Explore 25.5.255.1 · 1 digit left.
108L4depth=5 solutions=1Complete, but not a solution: dead end.
109L11depth=4 solutions=1Unchoose "1": back to 25.5.255 · 2 digits left.
110L9depth=4 solutions=1Choose "11".
111L1depth=5 solutions=1Explore 25.5.255.11 · 0 digits left.
112L3depth=5 solutions=2Complete and valid: record 25.5.255.11.
113L11depth=4 solutions=2Unchoose "11": back to 25.5.255 · 2 digits left.
114L11depth=3 solutions=2Unchoose "255": back to 25.5 · 5 digits left.
115L11depth=2 solutions=2Unchoose "5": back to 25 · 6 digits left.
116L9depth=2 solutions=2Choose "52".
117L1depth=3 solutions=2Explore 25.52 · 4 digits left.
118L9depth=3 solutions=2Choose "5".
119L1depth=4 solutions=2Explore 25.52.5 · 3 digits left.
120L9depth=4 solutions=2Choose "5".
121L1depth=5 solutions=2Explore 25.52.5.5 · 2 digits left.
122L4depth=5 solutions=2Complete, but not a solution: dead end.
123L11depth=4 solutions=2Unchoose "5": back to 25.52.5 · 3 digits left.
124L9depth=4 solutions=2Choose "51".
125L1depth=5 solutions=2Explore 25.52.5.51 · 1 digit left.
126L4depth=5 solutions=2Complete, but not a solution: dead end.
127L11depth=4 solutions=2Unchoose "51": back to 25.52.5 · 3 digits left.
128L8depth=5 solutions=2Reject "511": 511 is above 255.
129L11depth=3 solutions=2Unchoose "5": back to 25.52 · 4 digits left.
130L9depth=3 solutions=2Choose "55".
131L1depth=4 solutions=2Explore 25.52.55 · 2 digits left.
132L9depth=4 solutions=2Choose "1".
133L1depth=5 solutions=2Explore 25.52.55.1 · 1 digit left.
134L4depth=5 solutions=2Complete, but not a solution: dead end.
135L11depth=4 solutions=2Unchoose "1": back to 25.52.55 · 2 digits left.
136L9depth=4 solutions=2Choose "11".
137L1depth=5 solutions=2Explore 25.52.55.11 · 0 digits left.
138L3depth=5 solutions=3Complete and valid: record 25.52.55.11.
139L11depth=4 solutions=3Unchoose "11": back to 25.52.55 · 2 digits left.
140L11depth=3 solutions=3Unchoose "55": back to 25.52 · 4 digits left.
141L8depth=4 solutions=3Reject "551": 551 is above 255.
142L11depth=2 solutions=3Unchoose "52": back to 25 · 6 digits left.
143L8depth=3 solutions=3Reject "525": 525 is above 255.
144L11depth=1 solutions=3Unchoose "25": back to (nothing yet) · 8 digits left.
145L9depth=1 solutions=3Choose "255".
146L1depth=2 solutions=3Explore 255 · 5 digits left.
147L9depth=2 solutions=3Choose "2".
148L1depth=3 solutions=3Explore 255.2 · 4 digits left.
149L9depth=3 solutions=3Choose "5".
150L1depth=4 solutions=3Explore 255.2.5 · 3 digits left.
151L9depth=4 solutions=3Choose "5".
152L1depth=5 solutions=3Explore 255.2.5.5 · 2 digits left.
153L4depth=5 solutions=3Complete, but not a solution: dead end.
154L11depth=4 solutions=3Unchoose "5": back to 255.2.5 · 3 digits left.
155L9depth=4 solutions=3Choose "51".
156L1depth=5 solutions=3Explore 255.2.5.51 · 1 digit left.
157L4depth=5 solutions=3Complete, but not a solution: dead end.
158L11depth=4 solutions=3Unchoose "51": back to 255.2.5 · 3 digits left.
159L8depth=5 solutions=3Reject "511": 511 is above 255.
160L11depth=3 solutions=3Unchoose "5": back to 255.2 · 4 digits left.
161L9depth=3 solutions=3Choose "55".
162L1depth=4 solutions=3Explore 255.2.55 · 2 digits left.
163L9depth=4 solutions=3Choose "1".
164L1depth=5 solutions=3Explore 255.2.55.1 · 1 digit left.
165L4depth=5 solutions=3Complete, but not a solution: dead end.
166L11depth=4 solutions=3Unchoose "1": back to 255.2.55 · 2 digits left.
167L9depth=4 solutions=3Choose "11".
168L1depth=5 solutions=3Explore 255.2.55.11 · 0 digits left.
169L3depth=5 solutions=4Complete and valid: record 255.2.55.11.
170L11depth=4 solutions=4Unchoose "11": back to 255.2.55 · 2 digits left.
171L11depth=3 solutions=4Unchoose "55": back to 255.2 · 4 digits left.
172L8depth=4 solutions=4Reject "551": 551 is above 255.
173L11depth=2 solutions=4Unchoose "2": back to 255 · 5 digits left.
174L9depth=2 solutions=4Choose "25".
175L1depth=3 solutions=4Explore 255.25 · 3 digits left.
176L9depth=3 solutions=4Choose "5".
177L1depth=4 solutions=4Explore 255.25.5 · 2 digits left.
178L9depth=4 solutions=4Choose "1".
179L1depth=5 solutions=4Explore 255.25.5.1 · 1 digit left.
180L4depth=5 solutions=4Complete, but not a solution: dead end.
181L11depth=4 solutions=4Unchoose "1": back to 255.25.5 · 2 digits left.
182L9depth=4 solutions=4Choose "11".
183L1depth=5 solutions=4Explore 255.25.5.11 · 0 digits left.
184L3depth=5 solutions=5Complete and valid: record 255.25.5.11.
185L11depth=4 solutions=5Unchoose "11": back to 255.25.5 · 2 digits left.
186L11depth=3 solutions=5Unchoose "5": back to 255.25 · 3 digits left.
187L9depth=3 solutions=5Choose "51".
188L1depth=4 solutions=5Explore 255.25.51 · 1 digit left.
189L9depth=4 solutions=5Choose "1".
190L1depth=5 solutions=5Explore 255.25.51.1 · 0 digits left.
191L3depth=5 solutions=6Complete and valid: record 255.25.51.1.
192L11depth=4 solutions=6Unchoose "1": back to 255.25.51 · 1 digit left.
193L11depth=3 solutions=6Unchoose "51": back to 255.25 · 3 digits left.
194L8depth=4 solutions=6Reject "511": 511 is above 255.
195L11depth=2 solutions=6Unchoose "25": back to 255 · 5 digits left.
196L9depth=2 solutions=6Choose "255".
197L1depth=3 solutions=6Explore 255.255 · 2 digits left.
198L9depth=3 solutions=6Choose "1".
199L1depth=4 solutions=6Explore 255.255.1 · 1 digit left.
200L9depth=4 solutions=6Choose "1".
201L1depth=5 solutions=6Explore 255.255.1.1 · 0 digits left.
202L3depth=5 solutions=7Complete and valid: record 255.255.1.1.
203L11depth=4 solutions=7Unchoose "1": back to 255.255.1 · 1 digit left.
204L11depth=3 solutions=7Unchoose "1": back to 255.255 · 2 digits left.
205L9depth=3 solutions=7Choose "11".
206L1depth=4 solutions=7Explore 255.255.11 · 0 digits left.
207L11depth=3 solutions=7Unchoose "11": back to 255.255 · 2 digits left.
208L11depth=2 solutions=7Unchoose "255": back to 255 · 5 digits left.
209L11depth=1 solutions=7Unchoose "255": back to (nothing yet) · 8 digits left.
210L13solutions=7 nodes=69 return=[2.55.255.11, 25.5.255.11, 25.52.55.11, 255.2.55.11, 255.25.5.11, 255.25.51.1, 255.255.1.1]Search finished: 7 solutions, 68 choices tried. Backtracking explores every branch the pruning rules allow.
20.05.5

Word break enumeration

Given a string and a dictionary, list every way to split the string into dictionary words. At each position try every prefix; recurse only if the prefix is a word. For "catsand" with {cat, cats, and, sand} the sentences are "cat sand" and "cats and". The number of sentences can be exponential (the string "aaaa" with {a, aa, aaa} has 7), so listing them is inherently expensive.

If you only need to know *whether* a split exists, memoise on the start index and the problem becomes O(n²) (Chapter 24). The animation rejects non-words with ✗ and the dictionary reason.

Java
1static List<String> wordBreak(String s, Set<String> dict) {
2 List<String> out = new ArrayList<>();
3 split(s, 0, dict, new ArrayList<>(), out);
4 return out;
5}
6private static void split(String s, int start, Set<String> dict, List<String> words, List<String> out) {
7 if (start == s.length()) { out.add(String.join(" ", words)); return; }
8 for (int end = start + 1; end <= s.length(); end++) {
9 String word = s.substring(start, end);
10 if (!dict.contains(word)) continue; // prune: only dictionary words
11 words.add(word);
12 split(s, end, dict, words, out);
13 words.remove(words.size() - 1);
14 }
15}

Word break: enumerate every sentence

Interactive · edit the input

Your input

Try

partial solution

"" + "catsand" left

solutions found (0)

none yet
Setup

Split "catsand" into words from {cat, cats, and, sand}. At each position try every prefix; only dictionary words are explored. Without the check the tree would try all 2^6 ways to cut the string.

Step 1 / 27
Java
1static void wordBreak(String s, int start, Set<String> dict, List<String> words, List<String> out) {
2 if (start == s.length()) { out.add(String.join(" ", words)); return; }
3 for (int end = start + 1; end <= s.length(); end++) {
4 String word = s.substring(start, end);
5 if (!dict.contains(word)) continue; // prune: only dictionary words
6 words.add(word); // choose
7 wordBreak(s, end, dict, words, out); // explore
8 words.remove(words.size() - 1); // unchoose
9 }
10}

Live state

depth

0

solutions

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 27 steps
StepLineStateWhat happens
1L1depth=0 solutions=0Split "catsand" into words from {cat, cats, and, sand}. At each position try every prefix; only dictionary words are explored. Without the check the tree would try all 2^6 ways to cut the string.
2L1depth=1 solutions=0Explore "" + "catsand" left.
3L5depth=2 solutions=0Reject "c": "c" is not in the dictionary.
4L5depth=2 solutions=0Reject "ca": "ca" is not in the dictionary.
5L6depth=1 solutions=0Choose "cat".
6L1depth=2 solutions=0Explore "cat" + "sand" left.
7L5depth=3 solutions=0Reject "s": "s" is not in the dictionary.
8L5depth=3 solutions=0Reject "sa": "sa" is not in the dictionary.
9L5depth=3 solutions=0Reject "san": "san" is not in the dictionary.
10L6depth=2 solutions=0Choose "sand".
11L1depth=3 solutions=0Explore "cat sand" + "" left.
12L2depth=3 solutions=1Complete and valid: record "cat sand".
13L8depth=2 solutions=1Unchoose "sand": back to "cat" + "sand" left.
14L8depth=1 solutions=1Unchoose "cat": back to "" + "catsand" left.
15L6depth=1 solutions=1Choose "cats".
16L1depth=2 solutions=1Explore "cats" + "and" left.
17L5depth=3 solutions=1Reject "a": "a" is not in the dictionary.
18L5depth=3 solutions=1Reject "an": "an" is not in the dictionary.
19L6depth=2 solutions=1Choose "and".
20L1depth=3 solutions=1Explore "cats and" + "" left.
21L2depth=3 solutions=2Complete and valid: record "cats and".
22L8depth=2 solutions=2Unchoose "and": back to "cats" + "and" left.
23L8depth=1 solutions=2Unchoose "cats": back to "" + "catsand" left.
24L5depth=2 solutions=2Reject "catsa": "catsa" is not in the dictionary.
25L5depth=2 solutions=2Reject "catsan": "catsan" is not in the dictionary.
26L5depth=2 solutions=2Reject "catsand": "catsand" is not in the dictionary.
27L10solutions=2 nodes=15 return=["cat sand", "cats and"]Search finished: 2 solutions, 14 choices tried. Backtracking explores every branch the pruning rules allow.

Practice ladder

Now try it yourself

Easy

  • Letter Case Permutation
  • Binary Watch

Medium

  • Generate Parentheses
  • Letter Combinations of a Phone Number
  • Palindrome Partitioning
  • Restore IP Addresses

Advanced

  • Word Break II
  • Remove Invalid Parentheses
  • Expression Add Operators

Key takeaways

  • Build strings with StringBuilder: append to choose, deleteCharAt to unchoose.
  • Test a piece against its rule (palindrome, range, dictionary) before recursing.
  • The number of results can be exponential: counting or existence questions need memoisation instead.