Restore IP Addresses

Medium Backtracking

Problem

Given a string s containing only digits, return all possible valid IP address combinations that can be formed by inserting dots into s such that each segment is a valid IP octet.

  • 1 ≤ s.length ≤ 20
  • s consists only of digits.

Example

Input: s = "25525511135"
Output: ["255.255.11.135", "255.255.111.35"]

The brute-force approach would try all possible ways to insert three dots, resulting in many invalid IPs due to segment length or value constraints. The algorithm uses backtracking to build segments incrementally, pruning invalid paths early. For example, starting from index 0, it tries segments of length 1 to 3. It first tries '2', then recurses to build the next segment. When a segment is invalid (e.g., '256' or '00'), it backtracks immediately. When four valid segments are formed and the entire string is consumed, the current path is joined with dots and added to the result.

Approach

Straightforward Solution

A brute-force approach tries all ways to place three dots in the string, checking each resulting segment for validity. This leads to O(n^3) possibilities and is inefficient without pruning.

Core Observation

An IP address consists of exactly four segments, each between 0 and 255, with no leading zeros unless the segment is exactly '0'. The problem reduces to partitioning the string into four valid segments.

Path to Optimal

The key insight is to use backtracking to build segments one by one, pruning invalid segments early. By limiting segment length to 3 and checking validity immediately, the search space is drastically reduced. The recursion stops when four segments are formed or the string is exhausted, ensuring correctness and efficiency.

Optimal Approach

Use a recursive DFS that tries segments of length 1 to 3 starting from the current index. For each segment, check if it is valid (no leading zeros unless single '0', and integer value ≤ 255). If valid, append to the current path and recurse for the next segment. When four segments are formed and the entire string is consumed, join the segments with dots and add to the result. Backtrack by removing the last segment before trying the next possibility.

Time

O(1)

The maximum length of s is 20, and IP addresses have fixed 4 segments with max length 3 each, so the search space is bounded by a constant factor, making the algorithm effectively O(1) for practical input sizes.

Space

O(1)

The recursion stack depth is at most 4 (number of segments), and the path stores at most 4 segments. The output list size depends on the number of valid IPs but is not counted as auxiliary space.

Pattern Spotlight

Backtracking (State Restoration)

For problems requiring all valid partitions or combinations, use backtracking with early pruning and always restore state after exploring each choice to avoid side effects between branches.

Solution

1class Solution:
2 def restoreIpAddresses(self, s: str) -> List[str]:
3 res = []
4 path = []
5
6 def dfs(i):
7 if len(path) == 4:
8 if i == len(s):
9 res.append(".".join(path))
10 return
11
12 for l in range(1, 4):
13 if i + l > len(s):
14 break
15
16 seg = s[i:i+l]
17
18 if (seg[0] == '0' and l > 1) or int(seg) > 255:
19 continue
20
21 path.append(seg)
22 dfs(i + l)
23 path.pop()
24
25 dfs(0)
26 return res

Step-by-Step Solution

1

Build Valid IP Segments via Depth-First Search with Pruning

3res = []
4path = []
6def dfs(i):
7 if len(path) == 4:
8 if i == len(s):
9 res.append(".".join(path))
10 return
12 for l in range(1, 4):
13 if i + l > len(s):
14 break
16 seg = s[i:i+l]
18 if (seg[0] == '0' and l > 1) or int(seg) > 255:
19 continue
21 path.append(seg)
22 dfs(i + l)
23 path.pop()

Objective

To recursively explore all possible segmentations of the string into valid IP address segments, pruning invalid segments early.

Key Insight

Backtracking incrementally constructs segments by trying lengths 1 to 3 from the current index. Validity checks for leading zeros and numeric range immediately discard invalid segments, drastically reducing the search space. The recursion terminates when four segments are formed or the string is fully consumed, ensuring only valid IP addresses are collected. The path list acts as a temporary workspace that is restored after each recursive call to maintain correctness across branches.

Interview Quick-Check

Core Logic

Backtracking tries segments of length 1 to 3, pruning invalid segments (leading zeros or >255), and recurses to build the next segment until four valid segments cover the entire string.

State & Boundaries

The recursion stops when four segments are formed; if the entire string is consumed, the current path is a valid IP and added to results.

Common Pitfalls & Bugs

Failing to prune segments with leading zeros or values >255 leads to invalid IPs and incorrect results.

Complexity

Limiting segment length to 3 and pruning invalid segments early keeps the search space manageable despite the combinatorial nature.

2

Initiate Backtracking and Return All Valid IP Addresses

25dfs(0)
26return res

Objective

To start the recursive search from the beginning of the string and return the collected valid IP addresses.

Key Insight

The initial call to dfs(0) triggers the entire backtracking process. After exploring all valid segmentations, the accumulated results are returned. This clear separation between setup and recursion ensures the algorithm is clean and easy to understand.

Interview Quick-Check

Core Logic

Starting the recursion at index 0 initiates the exploration of all valid IP segmentations.

State & Boundaries

Returning the result list after recursion completes provides the final set of valid IP addresses.

Line Analysis

This solution has 4 Critical lines interviewers watch for.

Line 18 Critical
if (seg[0] == '0' and l > 1) or int(seg) > 255:

Check if the segment has a leading zero or exceeds 255.

This line is the critical pruning step that enforces IP address validity rules: segments cannot have leading zeros unless they are exactly '0', and must be ≤ 255. Without this check, invalid IPs would be generated, and the algorithm would waste time exploring invalid paths.

Line 23 Critical
path.pop()

Remove the last segment from the path to backtrack.

This line is the single most critical line in the backtracking algorithm; it restores the path state after recursion returns, enabling exploration of alternative branches without corrupting shared state.

Line 8 Critical
if i == len(s):

Check if the entire string has been consumed when four segments are formed.

Only if the entire string is used up do we have a valid IP address; partial consumption means invalid segmentation.

Line 22 Critical
dfs(i + l)

Recursively call dfs to build the next segment starting after the current segment.

This recursive call is the core of the backtracking algorithm, advancing the exploration by building the next segment from the updated index, enabling exhaustive search of all valid IP addresses.

Line 7 Core
if len(path) == 4:

Check if four segments have been formed.

An IP address must have exactly four segments; this condition identifies when a complete candidate is ready for validation.

Line 10 Core
return

Return from recursion after processing four segments.

Stopping recursion here prevents exploring invalid paths with more than four segments, maintaining correctness.

Line 13 Core
if i + l > len(s):

Break the loop if the segment exceeds the string length.

Preventing out-of-bounds slicing ensures safe substring extraction and avoids runtime errors.

Line 19 Core
continue

Skip invalid segments and continue to next iteration.

Continuing after invalid segment detection prevents further recursion down invalid paths, improving efficiency and correctness.

Line 9 Core
res.append(".".join(path))

Add the current valid IP address to the results by joining segments with dots.

Joining the segments into the standard IP address format and adding to results records a valid solution found by the recursion.

Line 12 Core
for l in range(1, 4):

Iterate over possible segment lengths from 1 to 3.

IP segments can be at most three digits; this loop tries all valid segment lengths from the current index.

Line 16 Core
seg = s[i:i+l]

Extract the current segment substring from the string.

Capturing the candidate segment allows validation and recursive exploration of this choice.

Line 21 Core
path.append(seg)

Add the valid segment to the current path.

Appending the segment to the path represents choosing this segment in the current recursive branch, enabling incremental construction of the IP address.

Line 6 Standard
def dfs(i):

Define the recursive DFS function to build IP segments starting from index i.

Encapsulating the backtracking logic in a helper function allows clean recursive exploration of all segmentations from any position in the string.

Line 14 Standard
break

Exit the loop early when segment length is invalid due to string length.

Breaking early avoids unnecessary iterations once the substring would be invalid, improving efficiency.

Line 25 Standard
dfs(0)

Start the backtracking process from index 0.

Initiating the recursion from the start of the string triggers the entire search for valid IP addresses.

Line 4 Standard
path = []

Initialize the current path list to store segments during recursion.

The path list holds the segments of the IP address currently being constructed, enabling incremental building and backtracking.

Line 26 Standard
return res

Return the list of all valid IP addresses found.

Returning the accumulated results provides the final output of the algorithm after exhaustive search.

Line 3 Basic
res = []

Initialize the list to store valid IP addresses.

This list accumulates all valid IP addresses found during the backtracking process, serving as the final output container.

Test Your Understanding

Why must the algorithm prune segments with leading zeros or values greater than 255 during backtracking?

Because IP address segments must be between 0 and 255 and cannot have leading zeros unless the segment is exactly '0'. Pruning invalid segments early prevents exploring invalid paths, ensuring correctness and efficiency.

Related Problems

Backtracking pattern

Don't just read it. Drill it.

Reconstruct Restore IP Addresses from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the Restore IP Addresses drill

or drill a free problem