String · 32 of 43
Program to generate all possible valid IP addresses from given string
Cut a string of digits into four numbers from 0 to 255, in every legal way, to make internet addresses.
- Simple way
- time O(n^3)
room O(n) - Clever way
- time O(3^4) = O(1)
room O(1)
The problem, simply
An IP address is four numbers joined by dots, like 192.168.1.1. Each number is from 0 to 255, and a number may not start with 0 unless it is just 0. So 1.0.20.3 is fine, but 1.02.0.3 and 1.0.256.3 are not.
You get a string of digits with no dots. Give back every IP address you can make by putting exactly three dots into it. You may not add, remove or reorder digits.
A story to remember it
Picture it
Every cut the search tries on 10203. Each level is one part. Red circles break a rule: they start with 0, leave extra digits, or leave too few digits for the parts still needed. After a part that starts with 0, longer parts are not even tried. The two double rings are the answers 1.0.20.3 and 10.2.0.3.
- answer
- rejected
Examples
A normal case
- In
s = '25525511135'- Out
['255.255.11.135', '255.255.111.35']
The last 5 digits 11135 can split as 11 and 135 or as 111 and 35. 1 and 1135 fails, since 1135 is too big.
Zeros
- In
s = '10203'- Out
['1.0.20.3', '10.2.0.3']
A lone 0 is fine as a part. 02, 03 and 020 are not.
Many answers
- In
s = '101023'- Out
['1.0.10.23', '1.0.102.3', '10.1.0.23', '10.10.2.3', '101.0.2.3']
Five different ways to cut work here.
All zeros
- In
s = '0000'- Out
['0.0.0.0']
Each part must be a single 0.
Too short
- In
s = '123'- Out
[]
Three digits cannot make four parts.
Way 1: the simple way
Try every place for the three dots. The first dot goes after position i, the second after j, the third after k, with i < j < k. For each choice, check all four parts.
- Loop
i,jandkover all cut points. - Cut the four parts and check each: 1 to 3 digits, no leading 0, at most 255.
- If all four pass, join them with dots and keep the address.
import java.util.*;
class Solution {
private boolean ok(String p) {
if (p.isEmpty() || p.length() > 3) return false;
if (p.length() > 1 && p.charAt(0) == '0') return false; // no leading zero
return Integer.parseInt(p) <= 255;
}
public List<String> restoreIpAddresses(String s) {
List<String> out = new ArrayList<>();
int n = s.length();
for (int i = 1; i < n; i++)
for (int j = i + 1; j < n; j++)
for (int k = j + 1; k < n; k++) {
String a = s.substring(0, i), b = s.substring(i, j);
String c = s.substring(j, k), d = s.substring(k);
if (ok(a) && ok(b) && ok(c) && ok(d)) {
out.add(a + "." + b + "." + c + "." + d);
}
}
return out;
}
}How long it takes: about n^3 / 6 ways to place three dots, each checked quickly, so O(n^3). Extra room: the four small part strings, O(n). It keeps trying dots far apart even when a part is clearly longer than 3 digits.
Way 2: the clever way
Cut the ribbon one part at a time with recursion, and stop a branch as soon as it cannot work. Each part has only 3 lengths to try, and there are only 4 parts, so the whole tree has at most 3^4 = 81 bottom ends, no matter what.
- If 4 parts are cut, keep the address only if no digits are left.
- If the digits left are fewer than the parts still needed, or more than 3 times that, stop.
- Try the next part with 1, 2 and 3 digits. Skip it if it starts with 0 (and is longer than one digit) or is above 255.
- Add the part, go deeper, then remove it again.
Cutting 10203 into four parts
01 / 17
Start with the digits 10203. We need 4 parts. Each part takes 1, 2 or 3 digits.
- parts
- none
- answers
- 0
- looking now
- seen
- done
- answer
- rejected
- path
import java.util.*;
class Solution {
public List<String> restoreIpAddresses(String s) {
List<String> out = new ArrayList<>();
cut(s, 0, new ArrayList<>(), out);
return out;
}
private void cut(String s, int start, List<String> parts, List<String> out) {
if (parts.size() == 4) {
if (start == s.length()) out.add(String.join(".", parts));
return;
}
int left = s.length() - start, need = 4 - parts.size();
if (left < need || left > need * 3) return; // cannot fit, stop early
for (int len = 1; len <= 3 && start + len <= s.length(); len++) {
String p = s.substring(start, start + len);
if (len > 1 && p.charAt(0) == '0') break; // 0 can only stand alone
if (Integer.parseInt(p) > 255) break; // longer will be even bigger
parts.add(p);
cut(s, start + len, parts, out);
parts.remove(parts.size() - 1); // undo
}
}
}How long it takes: at most 81 bottom ends, each joined in at most 15 characters, so O(3^4), which is a constant, O(1). Extra room: the recursion is 4 levels deep, O(1), not counting the answers.
Watch out for
- Do not stop at 4 parts if digits are left over.
1.0.2.0from10203leaves a3behind and is not an answer. Integer.parseInton a long part can overflow. Checking the length is at most 3 first keeps it safe.- A string longer than 12 or shorter than 4 digits has no answers. The pruning catches this right away.
- The answer may be an empty list. Do not return
null.
What interviewers ask next
- Why
breakinstead ofcontinuein the loop? A part that starts with 0 stays bad when you add digits, and a number above 255 only gets bigger. Nothing longer can help. - How many answers can there be at most? Small. Every address is decided by 3 part lengths of 1 to 3, so at most 27 for one string, and far fewer in practice.
- Validate one IPv4 or IPv6 address instead? Split on dots or colons, then check each part by its own rules (IPv6 parts are 1 to 4 hex digits).
- The same skeleton with a different rule? Splitting a string into words from a dictionary, or into palindromes, uses the exact same "cut a piece, check it, recurse, undo" loop.
Remember this
Grown-up words: cutting the ribbon is partitioning the string, the tree of cuts is a recursion tree, stopping early is pruning, and the whole method is backtracking.