Restore IP Addresses
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
s = "25525511135"["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
| 1 | class 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
Build Valid IP Segments via Depth-First Search with Pruning
| 3 | res = [] |
| 4 | path = [] |
| 6 | def 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.
Initiate Backtracking and Return All Valid IP Addresses
| 25 | dfs(0) |
| 26 | return 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.
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.
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.
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.
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.
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.
return
Return from recursion after processing four segments.
Stopping recursion here prevents exploring invalid paths with more than four segments, maintaining correctness.
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.
continue
Skip invalid segments and continue to next iteration.
Continuing after invalid segment detection prevents further recursion down invalid paths, improving efficiency and correctness.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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