93. Restore IP Addresses - Explanation

Problem Link

Description

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.

You are 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 = "101023"

Output: ["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]

Example 2:

Input: s = "010010"

Output: ["0.10.0.10","0.100.1.0"]

Constraints:

  • 1 <= s.length <= 20
  • s consists of digits only.


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Backtracking - Exploring all possible combinations by making choices and undoing them when they lead to invalid solutions
  • Recursion - Breaking down the problem into smaller subproblems (placing one IP segment at a time)
  • String Manipulation - Extracting and validating substrings for IP segment constraints

1. Backtracking

Intuition

A valid IP address has exactly four segments, each containing 1 to 3 digits with a value between 0 and 255. Leading zeros are not allowed except for the segment "0" itself. Backtracking lets us explore all possible ways to place three dots in the string. At each step, we try taking 1, 2, or 3 characters for the current segment, validate it, and recurse for the remaining segments.

Algorithm

  1. If the string length exceeds 12, return an empty list (maximum valid length is 12 digits).
  2. Define a recursive function that tracks the current position i, number of segments placed dots, and the IP being built curIP.
  3. Base case: if 4 segments are placed and we have consumed the entire string, add the IP to results.
  4. For each call, try segment lengths of 1, 2, and 3 characters starting at the current position.
  5. Skip segments with leading zeros (unless the segment is just "0") and values >= 256.
  6. Recurse with the next position, incremented segment count, and updated IP string.
  7. Return all valid IP addresses found.
class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        res = []
        if len(s) > 12:
            return res

        def backtrack(i, dots, curIP):
            if dots == 4 and i == len(s):
                res.append(curIP[:-1])
                return
            if dots > 4:
                return

            for j in range(i, min(i + 3, len(s))):
                if i != j and s[i] == "0":
                    continue
                if int(s[i: j + 1]) < 256:
                    backtrack(j + 1, dots + 1, curIP + s[i: j + 1] + ".")

        backtrack(0, 0, "")
        return res

Time & Space Complexity

  • Time complexity: O(mnn)O(m ^ n * n)
  • Space complexity: O(mn)O(m * n)

Where mm is equals to 33 as there are at most three digits in a valid segment and nn is equals to 44 as there are four segments in a valid IP.


2. Iteration

Intuition

Since there are exactly four segments and each can have 1, 2, or 3 characters, we can enumerate all 81 combinations (3^4) of segment lengths directly. For each combination, we check if the total length matches the input string and whether each resulting segment is valid. This avoids recursion overhead while still exploring all possibilities.

Algorithm

  1. If the string length exceeds 12, return an empty list.
  2. Use four nested loops, each iterating from 1 to 3 (representing segment lengths seg1, seg2, seg3, seg4).
  3. If the sum of all four segment lengths does not equal the string length, skip this combination.
  4. Extract the four substrings based on the current segment lengths.
  5. Validate each segment: no leading zeros (unless single digit) and value <= 255.
  6. If all segments are valid, join them with dots and add to res.
  7. Return all valid IP addresses found.
class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        res = []
        if len(s) > 12:
            return res

        def valid(num):
            return len(num) == 1 or (int(num) < 256 and num[0] != "0")

        def add(s1, s2, s3, s4):
            if s1 + s2 + s3 + s4 != len(s):
                return

            num1 = s[:s1]
            num2 = s[s1:s1+s2]
            num3 = s[s1+s2:s1+s2+s3]
            num4 = s[s1+s2+s3:]
            if valid(num1) and valid(num2) and valid(num3) and valid(num4):
                res.append(num1 + "." + num2 + "." + num3 + "." + num4)

        for seg1 in range(1, 4):
            for seg2 in range(1, 4):
                for seg3 in range(1, 4):
                    for seg4 in range(1, 4):
                        add(seg1, seg2, seg3, seg4)

        return res

Time & Space Complexity

  • Time complexity: O(mnn)O(m ^ n * n)
  • Space complexity: O(mn)O(m * n)

Where mm is equals to 33 as there are at most three digits in a valid segment and nn is equals to 44 as there are four segments in a valid IP.


Common Pitfalls

Allowing Leading Zeros in Multi-Digit Segments

Segments like "01" or "001" are invalid in IP addresses, but "0" alone is valid. The validation must specifically reject multi-digit segments that start with zero while accepting single-digit zeros.

Missing the Upper Bound Check for Segment Values

Each segment must be at most 255. Forgetting to check this constraint or using < 256 instead of <= 255 can lead to subtle bugs, especially for three-digit segments like "256" which appear valid at first glance.

Not Validating Total String Length Early

An IP address can have at most 12 digits (four segments of 3 digits each) and at least 4 digits (four segments of 1 digit each). Failing to check these bounds upfront leads to unnecessary computation on inputs that cannot possibly form valid IPs.