Restore IP Addresses solution and explanation
9 more videos on this problem
Restore IP Addresses - Leetcode 93 - PythonNeetCode · 17:44 · 61K views
Restore IP Addresses | Recursive Tree | Diagram | META | Leetcode 93codestorywithMIK · 38:01 · 18K views
93. Restore IP Addresses | LEETCODE BACKTRACKING | LEETCODE MEDIUM | CODE EXPLAINERcode Explainer · 7:52 · 7K views
Restore IP addresses problem (LeetCode #93) - Inside codeInside code · 9:42 · 5K views
[Java] Leetcode 93. Restore IP Addresses [Backtracking #3]Eric Programming · 21:48 · 3K views
Leetcode 93. Restore IP Addresses | Easy Peasy (Meta) | CodingDecoded SDE sheetCoding Decoded · 13:56 · 2K views
LeetCode 93 Restore IP AddressesJeevan Kumar - Cracckify · 6:46 · 2K views
LeetCode 93. Restore IP Addresses Explanation and Solutionhappygirlzt · 8:49 · 1K views
Can You Restore IP Addresses? (LeetCode 93: Restore IP Addresses)Code With Zi · 17:25 · 1K views
93. Restore IP Addresses | JavaScript | BackTracking | LeetCode Daily ChallengeLeetCodeWithMonu · 15:49 · 885 views
The problem
A valid IP address consists of exactly four integers separated by single dots. Each integer is between 0 and 255 (inclusive) and cannot have leading zeros.
- For example,
"0.1.2.201"and"192.168.1.1"are valid IP addresses, but"0.011.255.245","192.168.1.312"and"192.168@1.1"are invalid IP addresses.
Given a string s containing only digits, return all possible valid IP addresses that can be formed by inserting dots into s. You are not allowed to reorder or remove any digits in s. You may return the valid IP addresses in any order.
Example 1:
Input: s = "25525511135" Output: ["255.255.11.135","255.255.111.35"]
Example 2:
Input: s = "0000" Output: ["0.0.0.0"]
Example 3:
Input: s = "101023" Output: ["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
Constraints:
1 <= s.length <= 20sconsists of digits only.
Approach overview
Problem Overview: Given a string containing only digits, generate all possible valid IPv4 addresses by inserting three dots. Each address must contain exactly four segments, and each segment must be between 0 and 255 without leading zeros (except the number 0 itself).
Approach 1: Backtracking (O(n^3) time, O(1) extra space)
This approach builds the IP address segment by segment using backtracking. Start from the first character and try to take 1, 2, or 3 digits as the next segment. After selecting a segment, validate it: the integer must be ≤255 and must not contain leading zeros like "01". If the segment is valid, recursively continue building the next part until four segments are formed.
The recursion stops when either four segments are created or the string is fully consumed. If both conditions match simultaneously, join the segments using dots and add the result to the output. Because each segment can only be length 1–3 and there are exactly four segments, the search space is small. This method systematically explores combinations while pruning invalid branches early.
This solution heavily relies on string slicing and validation checks. Interviewers expect candidates to reason about constraints such as segment length, leading zeros, and numeric bounds. The recursion depth is at most four, so auxiliary space remains constant aside from the output list.
Approach 2: Iterative Split Enumeration (O(n^3) time, O(1) extra space)
An alternative approach avoids recursion and instead enumerates all possible ways to place three dots using nested loops. Use three indices i, j, and k to represent split points in the string. These indices define four segments: s[0:i], s[i:j], s[j:k], and s[k:n]. Each segment must be length 1–3, so the loops only explore valid boundaries.
For every split combination, validate the four segments individually. Convert each substring to an integer and check the constraints: value ≤255 and no leading zeros unless the segment is exactly "0". If all segments pass validation, combine them into a dotted string and add it to the result list.
This method performs explicit enumeration instead of recursion, which some developers find easier to reason about during debugging. It still relies on careful string manipulation and boundary checks.
Recommended for interviews: The backtracking solution is the expected answer. It demonstrates control over recursive state exploration and pruning invalid candidates early. The iterative split method also works and shows strong reasoning about segment boundaries, but backtracking better reflects the pattern interviewers associate with constrained combinatorial generation problems.
Approach 1: Backtracking Approach
This approach utilizes backtracking to explore all possible placements of the three dots in the string. We attempt to generate valid IP segments recursively, ensuring each generated segment maintains the restrictions of a valid IP address. By backtracking, we can explore each possible path of placing dots and ensure we cover all potential valid configurations.
def restore_ip_addresses(s):
def backtrack(start, parts):
if len(parts) == 4:
if start == len(s):
result.append(".".join(parts))
return
for i in range(1, 4):
if start + i > len(s):
break
part = s[start:start+i]
if (len(part) > 1 and part[0] == '0') or (int(part) > 255):
continue
backtrack(start + i, parts + [part])
result = []
backtrack(0, [])
return result
# Example usage:
# restore_ip_addresses("25525511135")import java.util.*;
public class RestoreIPAddresses {
public List<String> restoreIpAddresses(String s) {
List<String> result = new ArrayList<>();
backtrack(result, new ArrayList<>(), s, 0);
return result;
}
private void backtrack(List<String> result, List<String> temp, String s, int start) {
if (temp.size() == 4) {
if (start == s.length()) {
result.add(String.join(".", temp));
}
return;
}
for (int len = 1; len <= 3; len++) {
if (start + len > s.length()) break;
String part = s.substring(start, start + len);
if ((part.startsWith("0") && part.length() > 1) || Integer.parseInt(part) > 255) continue;
temp.add(part);
backtrack(result, temp, s, start + len);
temp.remove(temp.size() - 1);
}
}
public static void main(String[] args) {
RestoreIPAddresses rip = new RestoreIPAddresses();
System.out.println(rip.restoreIpAddresses("25525511135"));
}
}This Python function defines a helper function backtrack, which takes the current starting index and a list of constructed IP parts. We recursively attempt to build valid segments, ensuring they adhere to IP rules (no leading zeros, within [0, 255] range), and append valid results to the result list. We explore segments of lengths 1 to 3, backtracking appropriately.
Time Complexity: O(2^n), due to the exponential number of ways to split the string, though practical time for small strings like ours is quite feasible.
Space Complexity: O(n), for the recursion stack and to store the IP parts temporarily.
Approach 2: Iterative Approach
In this approach, we iterate through potential splits for the three dots required to form four IP sections. For each potential set of positions, we validate and construct the IP address. Although typically not as elegant as the recursive backtracking solution, it's systematic and allows us to explore and validate possible IP addresses through combinatorial checks.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector<string> restoreIpAddresses(string s) {
vector<string> result;
int n = s.size();
for (int i = 1; i < 4 && i < n - 2; ++i) {
for (int j = i + 1; j < i + 4 && j < n - 1; ++j) {
for (int k = j + 1; k < j + 4 && k < n; ++k) {
string s1 = s.substr(0, i);
string s2 = s.substr(i, j - i);
string s3 = s.substr(j, k - j);
string s4 = s.substr(k);
if (isValid(s1) && isValid(s2) && isValid(s3) && isValid(s4)) {
result.push_back(s1 + "." + s2 + "." + s3 + "." + s4);
}
}
}
}
return result;
}
bool isValid(const string &s) {
if (s.size() > 3 || (s[0] == '0' && s.size() > 1) || stoi(s) > 255) {
return false;
}
return true;
}
int main() {
vector<string> result = restoreIpAddresses("25525511135");
for (const string &ip : result) {
cout << ip << endl;
}
return 0;
}function restoreIpAddresses(s) {
const result = [];
for (let i = 1; i < Math.min(4, s.length); ++i) {
for (let j = i + 1; j < i + Math.min(4, s.length - i); ++j) {
for (let k = j + 1; k < j + Math.min(4, s.length - j); ++k) {
if (k < s.length) {
const s1 = s.substring(0, i);
const s2 = s.substring(i, j);
const s3 = s.substring(j, k);
const s4 = s.substring(k);
if (isValid(s1) && isValid(s2) && isValid(s3) && isValid(s4)) {
result.push(`${s1}.${s2}.${s3}.${s4}`);
}
}
}
}
}
return result;
}
function isValid(segment) {
return !(segment.length > 3 || (segment[0] === '0' && segment.length > 1) || parseInt(segment, 10) > 255);
}
console.log(restoreIpAddresses('25525511135'));This C++ solution uses a three-level nested loop to estimate possible positions of dots for dividing the string into four parts. For each configuration, it calls a helper function isValid to check if each part is a valid IP section (not exceeding 255 and no leading zero unless the part is zero itself).
Time Complexity: O(n^3), given the triple nested loop for positions.
Space Complexity: O(1), ignoring the output list space.
Approach 3: DFS
We define a function dfs(i), which represents the list of IP addresses that can be formed starting from the ith position of string s.
The execution steps of function dfs(i) are as follows:
If i is greater than or equal to the length of string s, it means that we have completed the splicing of four segments of the IP address. At this point, we need to check whether it meets the requirements of the four segments of the IP address. If it does, add the current IP to the answer.
If i is less than the length of string s, it means that we still need to splice a segment of the IP address. At this point, we need to determine the value of this segment of the IP address. If the value is greater than 255, or the current position i is 0 and the value of several positions after i is greater than 0, it means that it does not meet the requirements, so we return directly. Otherwise, add it to the IP address list, and continue to search for the next segment of the IP address.
The time complexity is O(n times 3^4), and the space complexity is O(n). Here, n is the length of string s.
function restoreIpAddresses(s: string): string[] {
const n = s.length;
const ans: string[] = [];
const t: string[] = [];
const dfs = (i: number): void => {
if (i >= n && t.length === 4) {
ans.push(t.join('.'));
return;
}
if (i >= n || t.length === 4) {
return;
}
let x = 0;
for (let j = i; j < i + 3 && j < n; ++j) {
x = x * 10 + s[j].charCodeAt(0) - '0'.charCodeAt(0);
if (x > 255 || (j > i && s[i] === '0')) {
break;
}
t.push(x.toString());
dfs(j + 1);
t.pop();
}
};
dfs(0);
return ans;
}public class Solution {
private IList<string> ans = new List<string>();
private IList<string> t = new List<string>();
private int n;
private string s;
public IList<string> RestoreIpAddresses(string s) {
n = s.Length;
this.s = s;
dfs(0);
return ans;
}
private void dfs(int i) {
if (i >= n && t.Count == 4) {
ans.Add(string.Join(".", t));
return;
}
if (i >= n || t.Count == 4) {
return;
}
int x = 0;
for (int j = i; j < i + 3 && j < n; ++j) {
x = x * 10 + (s[j] - '0');
if (x > 255 || (j > i && s[i] == '0')) {
break;
}
t.Add(x.ToString());
dfs(j + 1);
t.RemoveAt(t.Count - 1);
}
}
}func restoreIpAddresses(s string) (ans []string) {
n := len(s)
t := []string{}
var dfs func(int)
dfs = func(i int) {
if i >= n && len(t) == 4 {
ans = append(ans, strings.Join(t, "."))
return
}
if i >= n || len(t) == 4 {
return
}
x := 0
for j := i; j < i+3 && j < n; j++ {
x = x*10 + int(s[j]-'0')
if x > 255 || (j > i && s[i] == '0') {
break
}
t = append(t, s[i:j+1])
dfs(j + 1)
t = t[:len(t)-1]
}
}
dfs(0)
return
}Complexity comparison
| Approach | Complexity |
|---|---|
| Backtracking Approach | Time Complexity: O(2^n), due to the exponential number of ways to split the string, though practical time for small strings like ours is quite feasible. |
| Iterative Approach | Time Complexity: O(n^3), given the triple nested loop for positions. |
| DFS | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Backtracking | O(n^3) | O(1) auxiliary (depth ≤ 4) | Best general solution. Cleanly explores segment combinations with early pruning. |
| Iterative Split Enumeration | O(n^3) | O(1) | When recursion is avoided and you want explicit control over split indices. |
Frequently asked questions
Is Restore IP Addresses easy or hard?
Restore IP Addresses is classified as a Medium difficulty problem. The challenge comes from correctly handling constraints such as leading zeros, segment bounds (0–255), and ensuring exactly four segments while exploring combinations efficiently.
Restore IP Addresses Python/Java solution
Python and Java implementations usually follow the backtracking pattern. The function recursively chooses segment lengths from 1 to 3, validates each substring, and appends valid segments to a list. Once four segments are formed and the string is consumed, they are joined with dots to produce a valid IP address.
How to solve Restore IP Addresses in O(n)?
A true O(n) solution is not practical because the algorithm must check multiple combinations of segment boundaries. The closest practical complexity is O(n^3), achieved using backtracking or enumerating split indices. The constraints on segment length keep the actual runtime small in practice.
What is the best approach for Restore IP Addresses?
Backtracking is the most common and interview-friendly approach. It builds the IP address one segment at a time and prunes invalid segments early using constraints like value ≤255 and no leading zeros. Because an IP address has exactly four segments with maximum length three, the search space remains small and manageable.
Is Restore IP Addresses asked at Google/Amazon/Meta?
Restore IP Addresses is a common medium-level backtracking problem and appears frequently in interview preparation lists for companies like Amazon, Google, and Meta. It tests string manipulation, constraint validation, and recursive search techniques.
What data structure is used in Restore IP Addresses?
The core structure is a list or array used to store the current segments while building the IP address. The algorithm also relies on recursion (backtracking) or loop indices to explore possible splits, along with basic string slicing for segment validation.
What is the time complexity of Restore IP Addresses?
The typical complexity is O(n^3). Each segment can have length 1–3 and there are four segments, so the algorithm explores combinations of split positions while validating segments. The recursion depth never exceeds four, so the auxiliary space is O(1) excluding the output list.
