Bird
Raised Fist0
Interview PrepbacktrackingmediumAmazonFacebookGoogle

Restore IP Addresses

Choose your preparation mode4 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
🎯
Restore IP Addresses
mediumBACKTRACKINGAmazonFacebookGoogle

Imagine you receive a string of digits from a network packet, but the dots separating the IP address segments are missing. Your task is to restore all possible valid IP addresses from this string.

💡 This problem is about splitting a string into exactly four parts where each part is a valid IP segment. Beginners often struggle because they try to guess the splits without systematically exploring all possibilities and validating each segment carefully.
📋
Problem Statement

Given a string containing only digits, restore all possible valid IP address combinations. A valid IP address consists of exactly four integers (each between 0 and 255) separated by single dots, and no segment should contain leading zeros unless the segment is exactly '0'. Return all possible valid IP addresses in any order.

1 ≤ length of input string ≤ 20Input string contains only digits
💡
Example
Input"\"25525511135\""
Output["255.255.11.135", "255.255.111.35"]

The string can be split into these two valid IP addresses by placing dots at appropriate positions.

  • Input string length less than 4 → no valid IP addresses
  • Input string length greater than 12 → no valid IP addresses (since max 3 digits per segment and 4 segments)
  • Segments with leading zeros like '00' or '01' → invalid
  • Segment value greater than 255 → invalid
⚠️
Common Mistakes
Allowing segments with leading zeros like '00' or '01'

Generates invalid IP addresses that do not conform to IP rules

Add explicit check to reject segments starting with '0' unless the segment is exactly '0'

Not checking segment value > 255

Includes invalid IP segments like '256' or '999' in results

Convert segment to integer and reject if > 255

Not pruning based on remaining string length and segments

Causes unnecessary recursion leading to timeouts or inefficiency

Add pruning condition to check if remaining characters fit remaining segments

Adding IP address to results before confirming all 4 segments and full string consumption

Partial or incomplete IP addresses are included in output

Only add to results when exactly 4 segments are formed and entire string is used

Using global variables without resetting between test cases

Results from previous runs contaminate current output

Use local variables or clear global state before each run

🧠
Brute Force (Pure Recursion with Backtracking)
💡 This approach exists to build intuition by exploring all possible ways to split the string into four parts, validating each segment. It helps beginners understand the problem structure before optimizing.

Intuition

Try every possible split of the string into four parts by recursively choosing 1 to 3 digits for each segment, checking if each segment is valid, and backtracking if not.

Algorithm

  1. Start from the beginning of the string and try to take 1, 2, or 3 digits as the current segment.
  2. Check if the current segment is valid (not leading zero unless single '0', and ≤ 255).
  3. If valid, recurse to find the next segment until 4 segments are formed.
  4. If 4 segments are formed and the entire string is consumed, add the formed IP address to results.
💡 The challenge is to systematically try all splits and backtrack when a segment is invalid or when the number of segments exceeds four.
</>
Code
from typing import List

def restore_ip_addresses(s: str) -> List[str]:
    res = []
    def backtrack(start: int, path: List[str]):
        if len(path) == 4:
            if start == len(s):
                res.append(".".join(path))
            return
        for length in range(1, 4):
            if start + length > len(s):
                break
            segment = s[start:start+length]
            if (segment.startswith('0') and len(segment) > 1) or (length == 3 and int(segment) > 255):
                continue
            backtrack(start + length, path + [segment])
    backtrack(0, [])
    return res

# Driver code
if __name__ == "__main__":
    test_input = "25525511135"
    print(restore_ip_addresses(test_input))
Line Notes
def restore_ip_addresses(s: str) -> List[str]:Defines the main function with input string s and output list of strings
def backtrack(start: int, path: List[str]):Helper recursive function tracking current index and collected segments
if len(path) == 4:Base case: if 4 segments collected, check if entire string is used
if start == len(s):Only add to results if all characters are consumed
for length in range(1, 4):Try segments of length 1 to 3
if (segment.startswith('0') and len(segment) > 1) or (length == 3 and int(segment) > 255):Prune invalid segments early
backtrack(start + length, path + [segment])Recurse with updated start and path
return resReturn all valid IP addresses found
import java.util.*;

public class RestoreIPAddresses {
    public static List<String> restoreIpAddresses(String s) {
        List<String> res = new ArrayList<>();
        backtrack(s, 0, new ArrayList<>(), res);
        return res;
    }

    private static void backtrack(String s, int start, List<String> path, List<String> res) {
        if (path.size() == 4) {
            if (start == s.length()) {
                res.add(String.join(".", path));
            }
            return;
        }
        for (int len = 1; len <= 3; len++) {
            if (start + len > s.length()) break;
            String segment = s.substring(start, start + len);
            if ((segment.startsWith("0") && segment.length() > 1) || (len == 3 && Integer.parseInt(segment) > 255)) continue;
            path.add(segment);
            backtrack(s, start + len, path, res);
            path.remove(path.size() - 1);
        }
    }

    public static void main(String[] args) {
        String testInput = "25525511135";
        System.out.println(restoreIpAddresses(testInput));
    }
}
Line Notes
public static List<String> restoreIpAddresses(String s)Main method signature returning list of valid IPs
backtrack(s, 0, new ArrayList<>(), res);Start recursion from index 0 with empty path
if (path.size() == 4)Check if 4 segments collected
if (start == s.length())Add to results only if entire string consumed
for (int len = 1; len <= 3; len++)Try segments of length 1 to 3
if ((segment.startsWith("0") && segment.length() > 1) || (len == 3 && Integer.parseInt(segment) > 255)) continue;Skip invalid segments
path.add(segment);Choose current segment
path.remove(path.size() - 1);Backtrack by removing last segment
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    vector<string> restoreIpAddresses(string s) {
        vector<string> res;
        vector<string> path;
        backtrack(s, 0, path, res);
        return res;
    }

private:
    void backtrack(const string& s, int start, vector<string>& path, vector<string>& res) {
        if (path.size() == 4) {
            if (start == s.size()) {
                string ip = path[0];
                for (int i = 1; i < 4; i++) ip += "." + path[i];
                res.push_back(ip);
            }
            return;
        }
        for (int len = 1; len <= 3; len++) {
            if (start + len > s.size()) break;
            string segment = s.substr(start, len);
            if ((segment[0] == '0' && segment.size() > 1) || (len == 3 && stoi(segment) > 255)) continue;
            path.push_back(segment);
            backtrack(s, start + len, path, res);
            path.pop_back();
        }
    }
};

int main() {
    Solution sol;
    string testInput = "25525511135";
    vector<string> result = sol.restoreIpAddresses(testInput);
    for (auto& ip : result) cout << ip << endl;
    return 0;
}
Line Notes
vector<string> restoreIpAddresses(string s)Main function returning vector of valid IPs
void backtrack(const string& s, int start, vector<string>& path, vector<string>& res)Recursive helper with current index and path
if (path.size() == 4)Base case: 4 segments collected
if (start == s.size())Add IP if entire string consumed
for (int len = 1; len <= 3; len++)Try segments of length 1 to 3
if ((segment[0] == '0' && segment.size() > 1) || (len == 3 && stoi(segment) > 255)) continue;Skip invalid segments
path.push_back(segment);Choose segment
path.pop_back();Backtrack by removing last segment
function restoreIpAddresses(s) {
    const res = [];
    function backtrack(start, path) {
        if (path.length === 4) {
            if (start === s.length) {
                res.push(path.join('.'));
            }
            return;
        }
        for (let len = 1; len <= 3; len++) {
            if (start + len > s.length) break;
            const segment = s.substring(start, start + len);
            if ((segment.startsWith('0') && segment.length > 1) || (len === 3 && Number(segment) > 255)) continue;
            backtrack(start + len, [...path, segment]);
        }
    }
    backtrack(0, []);
    return res;
}

// Test
console.log(restoreIpAddresses("25525511135"));
Line Notes
function restoreIpAddresses(s)Main function to restore IP addresses
function backtrack(start, path)Recursive helper tracking index and segments
if (path.length === 4)Check if 4 segments collected
if (start === s.length)Add to results if entire string consumed
for (let len = 1; len <= 3; len++)Try segments of length 1 to 3
if ((segment.startsWith('0') && segment.length > 1) || (len === 3 && Number(segment) > 255)) continue;Skip invalid segments
backtrack(start + len, [...path, segment]);Recurse with new segment added
Complexity
TimeO(3^4) = O(81)
SpaceO(4) for recursion stack and path

At each of the 4 segments, we try up to 3 lengths, so 3^4 combinations. Each segment validation is O(1).

💡 For n=12 (max length), this means at most 81 recursive calls, which is manageable but not efficient for larger inputs.
Interview Verdict: Accepted but not optimal

This approach works and is easy to understand but can be improved by pruning and iterative methods.

🧠
Backtracking with Early Pruning
💡 This approach improves brute force by adding checks to prune impossible paths early, reducing unnecessary recursion and speeding up the search.

Intuition

Before recursing, check if the remaining string length fits the remaining segments (min 1 char per segment, max 3 chars per segment). If not, prune immediately.

Algorithm

  1. At each recursion, check if the remaining characters can fit into the remaining segments (between remaining_segments and 3 * remaining_segments).
  2. If not, return immediately to prune the search space.
  3. Otherwise, try segments of length 1 to 3, validate, and recurse.
  4. Add valid IP addresses to results when 4 segments are formed and string is fully consumed.
💡 The pruning condition is key to reducing the exponential search space by cutting off impossible branches early.
</>
Code
from typing import List

def restore_ip_addresses(s: str) -> List[str]:
    res = []
    def backtrack(start: int, path: List[str]):
        remaining_segments = 4 - len(path)
        remaining_chars = len(s) - start
        if remaining_chars < remaining_segments or remaining_chars > 3 * remaining_segments:
            return
        if len(path) == 4:
            if start == len(s):
                res.append(".".join(path))
            return
        for length in range(1, 4):
            if start + length > len(s):
                break
            segment = s[start:start+length]
            if (segment.startswith('0') and len(segment) > 1) or (length == 3 and int(segment) > 255):
                continue
            backtrack(start + length, path + [segment])
    backtrack(0, [])
    return res

# Driver code
if __name__ == "__main__":
    test_input = "25525511135"
    print(restore_ip_addresses(test_input))
Line Notes
remaining_segments = 4 - len(path)Calculate how many segments are left to fill
remaining_chars = len(s) - startCalculate how many characters remain in the string
if remaining_chars < remaining_segments or remaining_chars > 3 * remaining_segments:Prune if remaining chars can't fit the remaining segments
if len(path) == 4:Check if 4 segments collected
if (segment.startswith('0') and len(segment) > 1) or (length == 3 and int(segment) > 255):Validate segment early
backtrack(start + length, path + [segment])Recurse with chosen segment
import java.util.*;

public class RestoreIPAddresses {
    public static List<String> restoreIpAddresses(String s) {
        List<String> res = new ArrayList<>();
        backtrack(s, 0, new ArrayList<>(), res);
        return res;
    }

    private static void backtrack(String s, int start, List<String> path, List<String> res) {
        int remainingSegments = 4 - path.size();
        int remainingChars = s.length() - start;
        if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) return;
        if (path.size() == 4) {
            if (start == s.length()) {
                res.add(String.join(".", path));
            }
            return;
        }
        for (int len = 1; len <= 3; len++) {
            if (start + len > s.length()) break;
            String segment = s.substring(start, start + len);
            if ((segment.startsWith("0") && segment.length() > 1) || (len == 3 && Integer.parseInt(segment) > 255)) continue;
            path.add(segment);
            backtrack(s, start + len, path, res);
            path.remove(path.size() - 1);
        }
    }

    public static void main(String[] args) {
        String testInput = "25525511135";
        System.out.println(restoreIpAddresses(testInput));
    }
}
Line Notes
int remainingSegments = 4 - path.size();Calculate remaining segments to fill
int remainingChars = s.length() - start;Calculate remaining characters in string
if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) return;Prune impossible paths early
if (path.size() == 4)Check if 4 segments collected
if ((segment.startsWith("0") && segment.length() > 1) || (len == 3 && Integer.parseInt(segment) > 255)) continue;Validate segment
path.add(segment);Choose segment
path.remove(path.size() - 1);Backtrack
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    vector<string> restoreIpAddresses(string s) {
        vector<string> res;
        vector<string> path;
        backtrack(s, 0, path, res);
        return res;
    }

private:
    void backtrack(const string& s, int start, vector<string>& path, vector<string>& res) {
        int remainingSegments = 4 - path.size();
        int remainingChars = s.size() - start;
        if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) return;
        if (path.size() == 4) {
            if (start == s.size()) {
                string ip = path[0];
                for (int i = 1; i < 4; i++) ip += "." + path[i];
                res.push_back(ip);
            }
            return;
        }
        for (int len = 1; len <= 3; len++) {
            if (start + len > s.size()) break;
            string segment = s.substr(start, len);
            if ((segment[0] == '0' && segment.size() > 1) || (len == 3 && stoi(segment) > 255)) continue;
            path.push_back(segment);
            backtrack(s, start + len, path, res);
            path.pop_back();
        }
    }
};

int main() {
    Solution sol;
    string testInput = "25525511135";
    vector<string> result = sol.restoreIpAddresses(testInput);
    for (auto& ip : result) cout << ip << endl;
    return 0;
}
Line Notes
int remainingSegments = 4 - path.size();Calculate how many segments left
int remainingChars = s.size() - start;Calculate remaining characters
if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) return;Prune impossible paths
if (path.size() == 4)Check if 4 segments collected
if ((segment[0] == '0' && segment.size() > 1) || (len == 3 && stoi(segment) > 255)) continue;Validate segment
path.push_back(segment);Choose segment
path.pop_back();Backtrack
function restoreIpAddresses(s) {
    const res = [];
    function backtrack(start, path) {
        const remainingSegments = 4 - path.length;
        const remainingChars = s.length - start;
        if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) return;
        if (path.length === 4) {
            if (start === s.length) {
                res.push(path.join('.'));
            }
            return;
        }
        for (let len = 1; len <= 3; len++) {
            if (start + len > s.length) break;
            const segment = s.substring(start, start + len);
            if ((segment.startsWith('0') && segment.length > 1) || (len === 3 && Number(segment) > 255)) continue;
            backtrack(start + len, [...path, segment]);
        }
    }
    backtrack(0, []);
    return res;
}

// Test
console.log(restoreIpAddresses("25525511135"));
Line Notes
const remainingSegments = 4 - path.length;Calculate remaining segments
const remainingChars = s.length - start;Calculate remaining characters
if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) return;Prune impossible paths early
if (path.length === 4)Check if 4 segments collected
if ((segment.startsWith('0') && segment.length > 1) || (len === 3 && Number(segment) > 255)) continue;Validate segment
backtrack(start + len, [...path, segment]);Recurse with chosen segment
Complexity
TimeO(3^4) but with pruning reduces actual calls
SpaceO(4) recursion stack and path

Pruning reduces the number of recursive calls by eliminating impossible splits early, improving practical runtime.

💡 This pruning makes the algorithm faster in practice, especially for longer strings, by avoiding useless work.
Interview Verdict: Accepted and more efficient than brute force

This approach is preferred in interviews as it balances clarity and efficiency.

🧠
Iterative Backtracking with Stack
💡 This approach uses an explicit stack to simulate recursion, which can help avoid stack overflow and is useful in languages or environments with limited recursion depth.

Intuition

Use a stack to store partial solutions and indices, iteratively exploring all valid segments until four segments are formed or no more splits are possible.

Algorithm

  1. Initialize a stack with tuples containing start index, current path, and segment count.
  2. While stack not empty, pop the top and try to extend the path by 1 to 3 digit segments.
  3. Validate each segment and if valid, push new state onto stack.
  4. If 4 segments formed and string fully consumed, add to results.
💡 This approach mimics recursion but uses a loop and stack data structure to manage state explicitly.
</>
Code
from typing import List

def restore_ip_addresses(s: str) -> List[str]:
    res = []
    stack = [(0, [])]  # (start index, path)
    while stack:
        start, path = stack.pop()
        if len(path) == 4:
            if start == len(s):
                res.append(".".join(path))
            continue
        remaining_segments = 4 - len(path)
        remaining_chars = len(s) - start
        if remaining_chars < remaining_segments or remaining_chars > 3 * remaining_segments:
            continue
        for length in range(1, 4):
            if start + length > len(s):
                break
            segment = s[start:start+length]
            if (segment.startswith('0') and len(segment) > 1) or (length == 3 and int(segment) > 255):
                continue
            stack.append((start + length, path + [segment]))
    return res

# Driver code
if __name__ == "__main__":
    test_input = "25525511135"
    print(restore_ip_addresses(test_input))
Line Notes
stack = [(0, [])]Initialize stack with starting index and empty path
while stack:Process states until stack is empty
if len(path) == 4:Check if 4 segments collected
if start == len(s):Add to results if entire string consumed
remaining_segments = 4 - len(path)Calculate remaining segments
if remaining_chars < remaining_segments or remaining_chars > 3 * remaining_segments:Prune impossible states
for length in range(1, 4):Try segments of length 1 to 3
stack.append((start + length, path + [segment]))Push new state onto stack
import java.util.*;

public class RestoreIPAddresses {
    public static List<String> restoreIpAddresses(String s) {
        List<String> res = new ArrayList<>();
        Deque<State> stack = new ArrayDeque<>();
        stack.push(new State(0, new ArrayList<>()));
        while (!stack.isEmpty()) {
            State curr = stack.pop();
            int start = curr.start;
            List<String> path = curr.path;
            if (path.size() == 4) {
                if (start == s.length()) {
                    res.add(String.join(".", path));
                }
                continue;
            }
            int remainingSegments = 4 - path.size();
            int remainingChars = s.length() - start;
            if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) continue;
            for (int len = 1; len <= 3; len++) {
                if (start + len > s.length()) break;
                String segment = s.substring(start, start + len);
                if ((segment.startsWith("0") && segment.length() > 1) || (len == 3 && Integer.parseInt(segment) > 255)) continue;
                List<String> newPath = new ArrayList<>(path);
                newPath.add(segment);
                stack.push(new State(start + len, newPath));
            }
        }
        return res;
    }

    static class State {
        int start;
        List<String> path;
        State(int start, List<String> path) {
            this.start = start;
            this.path = path;
        }
    }

    public static void main(String[] args) {
        String testInput = "25525511135";
        System.out.println(restoreIpAddresses(testInput));
    }
}
Line Notes
Deque<State> stack = new ArrayDeque<>();Initialize stack to hold states
stack.push(new State(0, new ArrayList<>()));Push initial state with index 0 and empty path
while (!stack.isEmpty())Process states until stack empty
if (path.size() == 4)Check if 4 segments collected
if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) continue;Prune impossible states
for (int len = 1; len <= 3; len++)Try segments of length 1 to 3
stack.push(new State(start + len, newPath));Push new state with updated index and path
#include <iostream>
#include <vector>
#include <string>
#include <stack>
using namespace std;

class Solution {
public:
    vector<string> restoreIpAddresses(string s) {
        vector<string> res;
        struct State {
            int start;
            vector<string> path;
            State(int st, vector<string> p) : start(st), path(move(p)) {}
        };
        stack<State> stk;
        stk.push(State(0, {}));
        while (!stk.empty()) {
            State curr = stk.top();
            stk.pop();
            int start = curr.start;
            vector<string>& path = curr.path;
            if (path.size() == 4) {
                if (start == s.size()) {
                    string ip = path[0];
                    for (int i = 1; i < 4; i++) ip += "." + path[i];
                    res.push_back(ip);
                }
                continue;
            }
            int remainingSegments = 4 - (int)path.size();
            int remainingChars = (int)s.size() - start;
            if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) continue;
            for (int len = 1; len <= 3; len++) {
                if (start + len > (int)s.size()) break;
                string segment = s.substr(start, len);
                if ((segment[0] == '0' && segment.size() > 1) || (len == 3 && stoi(segment) > 255)) continue;
                vector<string> newPath = path;
                newPath.push_back(segment);
                stk.push(State(start + len, newPath));
            }
        }
        return res;
    }
};

int main() {
    Solution sol;
    string testInput = "25525511135";
    vector<string> result = sol.restoreIpAddresses(testInput);
    for (auto& ip : result) cout << ip << endl;
    return 0;
}
Line Notes
struct StateDefines a state with current index and path
stack<State> stk;Stack to hold states for iterative backtracking
stk.push(State(0, {}));Push initial state
if (path.size() == 4)Check if 4 segments collected
if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) continue;Prune impossible states
for (int len = 1; len <= 3; len++)Try segments of length 1 to 3
stk.push(State(start + len, newPath));Push new state with updated path
function restoreIpAddresses(s) {
    const res = [];
    const stack = [{ start: 0, path: [] }];
    while (stack.length > 0) {
        const { start, path } = stack.pop();
        if (path.length === 4) {
            if (start === s.length) {
                res.push(path.join('.'));
            }
            continue;
        }
        const remainingSegments = 4 - path.length;
        const remainingChars = s.length - start;
        if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) continue;
        for (let len = 1; len <= 3; len++) {
            if (start + len > s.length) break;
            const segment = s.substring(start, start + len);
            if ((segment.startsWith('0') && segment.length > 1) || (len === 3 && Number(segment) > 255)) continue;
            stack.push({ start: start + len, path: [...path, segment] });
        }
    }
    return res;
}

// Test
console.log(restoreIpAddresses("25525511135"));
Line Notes
const stack = [{ start: 0, path: [] }];Initialize stack with starting state
while (stack.length > 0)Process states until stack empty
if (path.length === 4)Check if 4 segments collected
if (remainingChars < remainingSegments || remainingChars > 3 * remainingSegments) continue;Prune impossible states
for (let len = 1; len <= 3; len++)Try segments of length 1 to 3
stack.push({ start: start + len, path: [...path, segment] });Push new state with updated path
Complexity
TimeO(3^4) with pruning
SpaceO(4) for stack and path storage

Similar to recursive backtracking but uses explicit stack to avoid recursion overhead and stack overflow.

💡 This iterative approach is useful in environments with recursion limits and helps understand recursion mechanics.
Interview Verdict: Accepted and safe for deep recursion cases

This approach is a good alternative when recursion depth is a concern or iterative solutions are preferred.

📊
All Approaches - One-Glance Tradeoffs
💡 In most interviews, the backtracking with pruning approach is the best balance of clarity and efficiency to implement.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(3^4) = O(81)O(4) recursion stackYes (deep recursion possible but limited here)YesMention only - never code due to inefficiency
2. Backtracking with Early PruningO(3^4) but pruned, faster in practiceO(4) recursion stackLowYesCode this approach in 95% of interviews
3. Iterative Backtracking with StackO(3^4) with pruningO(4) stack and path storageNoYesUse if recursion depth is a concern or interviewer requests iterative
💼
Interview Strategy
💡 Use this guide to understand the problem deeply before your interview. Start by clarifying the problem, then explain the brute force approach, optimize with pruning, and optionally discuss iterative solutions. Practice coding and testing thoroughly.

How to Present

Step 1: Clarify the problem and constraints with the interviewer.Step 2: Describe the brute force recursive backtracking approach.Step 3: Explain how pruning can reduce the search space.Step 4: Code the optimized backtracking solution.Step 5: Test with edge cases and discuss time/space complexity.Step 6: Optionally mention iterative backtracking if recursion depth is a concern.

Time Allocation

Clarify: 3min → Approach: 5min → Code: 10min → Test: 5min. Total ~23min

What the Interviewer Tests

The interviewer tests your understanding of backtracking, string partitioning, pruning invalid paths, and handling edge cases like leading zeros and segment bounds.

Common Follow-ups

  • What if the input string is very long? → Discuss pruning and iterative solutions.
  • Can you generate IP addresses in lexicographical order? → Sort results or modify traversal order.
💡 These follow-ups test your ability to optimize and adapt your solution to additional constraints or performance requirements.
🔍
Pattern Recognition

When to Use

Use this pattern when you need to split a string into a fixed number of parts with constraints on each part, and you want to generate all valid combinations.

Signature Phrases

"Restore all possible valid IP addresses""Split string into exactly four parts""Each part must be between 0 and 255""No leading zeros allowed"

NOT This Pattern When

Problems that require only counting partitions or greedy splitting without backtracking are different patterns.

Similar Problems

Generate Parentheses - similar recursive generation with constraintsPalindrome Partitioning - partition string into palindromesLetter Combinations of a Phone Number - combinatorial string generation

Practice

(1/5)
1. Consider the following BFS-based code snippet for removing invalid parentheses:
from collections import deque

def is_valid(s: str) -> bool:
    count = 0
    for c in s:
        if c == '(': count += 1
        elif c == ')':
            count -= 1
            if count < 0:
                return False
    return count == 0

def remove_invalid_parentheses_bfs(s: str):
    res = []
    visited = set([s])
    queue = deque([s])
    found = False

    while queue:
        size = len(queue)
        for _ in range(size):
            curr = queue.popleft()
            if is_valid(curr):
                res.append(curr)
                found = True
            if found:
                continue
            for i in range(len(curr)):
                if curr[i] not in ('(', ')'):
                    continue
                next_str = curr[:i] + curr[i+1:]
                if next_str not in visited:
                    visited.add(next_str)
                    queue.append(next_str)
    return res

print(remove_invalid_parentheses_bfs("()())()"))
What is the output of this code?
easy
A. ["()()()"]
B. ["()()()", "(())()"]
C. ["(())()"]
D. ["()())()"]

Solution

  1. Step 1: Trace BFS levels for input "()())()"

    Initial string is invalid. BFS removes one parenthesis at each position generating candidates. The first valid strings found are "()()()" and "(())()".
  2. Step 2: Confirm both valid strings are collected

    Since BFS stops adding new states after finding valid strings at current level, both minimal removal results are returned.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Both minimal valid strings are returned [OK]
Hint: BFS returns all minimal valid strings at first valid level [OK]
Common Mistakes:
  • Returning only one valid string
  • Returning original invalid string
  • Missing one minimal valid string
2. Consider the following snippet from an optimized Sudoku solver using backtracking with heuristics. Given the board below, what is the length of candidates for the first empty cell chosen after sorting empties by candidate count? Board: [['5', '3', '.', '.', '7', '.', '.', '.', '.'], ['6', '.', '.', '1', '9', '5', '.', '.', '.'], ['.', '9', '8', '.', '.', '.', '.', '6', '.'], ['8', '.', '.', '.', '6', '.', '.', '.', '3'], ['4', '.', '.', '8', '.', '3', '.', '.', '1'], ['7', '.', '.', '.', '2', '.', '.', '.', '6'], ['.', '6', '.', '.', '.', '.', '2', '8', '.'], ['.', '.', '.', '4', '1', '9', '.', '.', '5'], ['.', '.', '.', '.', '8', '.', '.', '7', '9']] Code snippet:
def get_candidates(r, c):
    b = (r//3)*3 + c//3
    return [ch for ch in '123456789' if ch not in rows[r] and ch not in cols[c] and ch not in boxes[b]]

empties.sort(key=lambda x: len(get_candidates(x[0], x[1])))
first_empty = empties[0]
candidates_len = len(get_candidates(first_empty[0], first_empty[1]))
What is the value of candidates_len?
easy
A. 2
B. 3
C. 4
D. 5

Solution

  1. Step 1: Identify first empty cell after sorting

    Check empties and compute candidates for each; the cell with fewest candidates is chosen first.
  2. Step 2: Calculate candidates for first empty cell (0,2)

    Row 0 has {'5','3','7'}, column 2 has {'8'}, box 0 has {'5','3','6','9','8'}. Candidates are digits not in any of these sets: '1','2','4'. So length is 3.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Counting candidates for (0,2) yields 3 [OK]
Hint: Count candidates by excluding row, column, box digits [OK]
Common Mistakes:
  • Forgetting box constraints
  • Miscounting candidates for first empty cell
3. What is the time complexity of the optimal in-place next permutation algorithm for an input array of length n?
medium
A. O(n) because it scans the array a constant number of times
B. O(n log n) because it sorts the suffix after the pivot
C. O(n!) due to generating all permutations
D. O(n^2) because it swaps elements multiple times in nested loops

Solution

  1. Step 1: Identify the main operations

    The algorithm scans from the end to find the pivot (O(n)), then scans again to find the swap element (O(n)), and finally reverses the suffix (O(n)).
  2. Step 2: Sum up the operations

    All steps are linear scans or swaps, so total time complexity is O(n).
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    No sorting or factorial generation involved, linear scans only [OK]
Hint: Next permutation scans array linearly multiple times [OK]
Common Mistakes:
  • Confusing suffix reversal with sorting
  • Assuming factorial due to permutations
4. Suppose the problem is modified so that digits can be repeated any number of times (reuse allowed), and you want to generate all letter combinations of length n from the given digits. Which modification to the algorithm correctly handles this reuse scenario?
hard
A. Modify backtracking to allow choosing the same digit multiple times by not incrementing the index after each choice until length n is reached
B. Sort digits and generate combinations by picking letters only once per digit
C. Use dynamic programming to store combinations of length k and build up to length n without reuse
D. Use the same iterative queue approach but do not advance the digit index; instead, for each combination, append letters from all digits repeatedly until length n is reached

Solution

  1. Step 1: Understand reuse requirement

    Allowing reuse means digits can be chosen multiple times to build combinations of length n.
  2. Step 2: Identify correct approach

    Backtracking can be modified to not increment the digit index after choosing a letter, allowing reuse until length n is reached.
  3. Step 3: Why other options fail

    Iterative queue approach as-is assumes fixed digit positions; DP without reuse does not handle repeated digits; sorting and picking once per digit ignores reuse.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Backtracking with flexible index handles reuse elegantly [OK]
Hint: Backtracking index controls reuse by not advancing [OK]
Common Mistakes:
  • Trying to reuse digits in iterative approach without index control
  • Ignoring reuse in DP or sorting
5. Suppose the N-Queens problem is modified so that queens can be placed multiple times in the same column (i.e., column conflicts are ignored), but diagonal conflicts still apply. Which modification to the bitmask backtracking approach correctly counts all valid solutions under this relaxed constraint?
hard
A. Track only column bitmask and ignore diagonal bitmasks
B. Keep all bitmasks but allow queens to be placed in any column regardless of conflicts
C. Remove the column bitmask and only track diagonal bitmasks for conflicts
D. Use a greedy approach placing queens in the first available diagonal position per row

Solution

  1. Step 1: Analyze relaxed constraints

    Column conflicts are ignored, so column bitmask is unnecessary; diagonal conflicts remain.
  2. Step 2: Modify bitmask tracking accordingly

    Remove column bitmask from conflict checks and recursive calls; keep positive and negative diagonal bitmasks to prune invalid placements.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Removing column mask matches relaxed rules and preserves diagonal conflict checks [OK]
Hint: Ignore column mask when column conflicts are allowed [OK]
Common Mistakes:
  • Ignoring diagonals too
  • Allowing all placements without pruning