StringProblem 32 of 43

Program to generate all possible valid IP addresses from given string

Brute Force
Time: O(n³)
Space: O(n)
Optimal
Time: O(n³)
Space: O(n)

Problem Statement

Given a string containing only digits, generate all possible valid IP addresses that can be formed by inserting dots. A valid IP address consists of exactly four integers (each from 0 to 255) separated by single dots, and cannot have leading zeros (except for the number 0 itself).

Example:

  • Input: "25525511135"
  • Output: ["255.255.11.135", "255.255.111.35"]

Example 2:

  • Input: "0000"
  • Output: ["0.0.0.0"]

Approach 1: Brute Force (Three Loops)

Intuition

An IP address has exactly 4 parts. Place 3 dots at all possible positions and validate each resulting IP address.

Algorithm

  1. Try all positions for the first dot (1 to min(3, n-3))
  2. Try all positions for the second dot
  3. Try all positions for the third dot
  4. Validate each of the 4 parts
  5. If valid, add to result
java
import java.util.*;

public class Solution {
    private boolean isValid(String s) {
        if (s.isEmpty() || s.length() > 3) return false;
        if (s.length() > 1 && s.charAt(0) == '0') return false;
        int num = Integer.parseInt(s);
        return num >= 0 && num <= 255;
    }
    
    public List<String> restoreIpAddresses(String s) {
        List<String> result = new ArrayList<>();
        int n = s.length();
        
        if (n < 4 || n > 12) return result;
        
        for (int i = 1; i <= 3 && i < n; i++) {
            for (int j = i + 1; j <= i + 3 && j < n; j++) {
                for (int k = j + 1; k <= j + 3 && k < n; k++) {
                    String part1 = s.substring(0, i);
                    String part2 = s.substring(i, j);
                    String part3 = s.substring(j, k);
                    String part4 = s.substring(k);
                    
                    if (isValid(part1) && isValid(part2) && 
                        isValid(part3) && isValid(part4)) {
                        result.add(part1 + "." + part2 + "." + 
                                  part3 + "." + part4);
                    }
                }
            }
        }
        
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(1) - Maximum 81 combinations (3³ * 3)
  • Space Complexity: O(1) - Not counting output

Approach 2: Backtracking (Optimal for Clarity)

Intuition

Use backtracking to build the IP address part by part. At each step, try adding 1, 2, or 3 digits as the next part.

Algorithm

  1. Start with empty current IP and 0 parts
  2. At each step, try taking 1-3 characters as the next part
  3. If valid, add to current and recurse
  4. When 4 parts are formed and string is exhausted, add to result
  5. Backtrack and try other combinations
java
import java.util.*;

public class Solution {
    private void backtrack(String s, int start, List<String> current, 
                          List<String> result) {
        // If we have 4 parts and used all characters
        if (current.size() == 4) {
            if (start == s.length()) {
                result.add(String.join(".", current));
            }
            return;
        }
        
        // Pruning
        int remaining = s.length() - start;
        int partsNeeded = 4 - current.size();
        if (remaining < partsNeeded || remaining > partsNeeded * 3) {
            return;
        }
        
        // Try taking 1, 2, or 3 characters
        for (int len = 1; len <= 3 && start + len <= s.length(); len++) {
            String part = s.substring(start, start + len);
            
            // Check for leading zeros
            if (part.length() > 1 && part.charAt(0) == '0') continue;
            
            // Check range
            if (Integer.parseInt(part) > 255) continue;
            
            current.add(part);
            backtrack(s, start + len, current, result);
            current.remove(current.size() - 1);
        }
    }
    
    public List<String> restoreIpAddresses(String s) {
        List<String> result = new ArrayList<>();
        backtrack(s, 0, new ArrayList<>(), result);
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(3⁴) = O(81) = O(1) - At most 81 combinations
  • Space Complexity: O(1) - Not counting output, just recursion stack of depth 4

Edge Cases and Validation


Dry Run Example

Input: "25525511135" Backtracking: ├── part1 = "2" │ ├── part2 = "5" -> remaining = 9, need 2 parts -> max 6, skip │ ├── part2 = "55" -> remaining = 7, need 2 parts -> max 6, skip │ └── part2 = "552" -> remaining = 6, need 2 parts -> ok │ ├── part3 = "5" ... │ └── ... ├── part1 = "25" │ └── ... └── part1 = "255" ├── part2 = "2" ... ├── part2 = "25" ... └── part2 = "255" ├── part3 = "1" -> part4 = "1135" > 255, invalid ├── part3 = "11" -> part4 = "135", valid! -> "255.255.11.135" └── part3 = "111" -> part4 = "35", valid! -> "255.255.111.35" Output: ["255.255.11.135", "255.255.111.35"]

Key Takeaways

  1. Constant time complexity - Despite recursion, max iterations are bounded
  2. Pruning is crucial - Skip invalid branches early (remaining chars check)
  3. Leading zeros are tricky - "01" is invalid, but "0" is valid
  4. Three nested loops is cleaner for this specific problem
  5. Backtracking approach is more generalizable
  6. This is a common interview problem testing string manipulation and validation