Bird
Raised Fist0
Interview PrepbacktrackinghardAmazonGoogleFacebook

Permutation Sequence (Kth)

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
🎯
Permutation Sequence (Kth)
hardBACKTRACKINGAmazonGoogleFacebook

Imagine you have a list of tasks to perform in every possible order, but you want to quickly jump to the k-th order without enumerating all permutations.

💡 This problem asks for the k-th permutation sequence of numbers from 1 to n. Beginners often struggle because generating all permutations is expensive and unnecessary. Understanding how to directly compute the k-th permutation using factorial math is key.
📋
Problem Statement

Given n and k, return the k-th permutation sequence of the numbers [1, 2, ..., n] in lexicographical order. Input: two integers n and k. Output: a string representing the k-th permutation.

1 ≤ n ≤ 91 ≤ k ≤ n!
💡
Example
Input"n = 3, k = 3"
Output"213"

The permutations of [1,2,3] in order are: 123, 132, 213, 231, 312, 321. The 3rd is '213'.

  • n = 1, k = 1 → output '1' (smallest input)
  • k = 1 → output is the first permutation in ascending order
  • k = n! → output is the last permutation in descending order
  • n = 9, k = 362880 (9!) → largest input within constraints
⚠️
Common Mistakes
Forgetting to convert k to zero-based index

Incorrect index calculation leads to wrong permutation

Always do k = k - 1 before calculations

Not removing used numbers from the list

Repeated numbers appear in the result

Remove the selected number from the available list after choosing

Using factorial values incorrectly or not precomputing

Wrong index calculation or inefficient repeated factorial computation

Precompute factorials once before main loop

Trying to generate all permutations for large n

Solution times out or crashes due to memory/time limits

Use direct computation approach instead of brute force

Not handling edge cases like k=1 or k=n!

Incorrect output or runtime errors

Test and handle boundary cases explicitly if needed

🧠
Brute Force (Generate All Permutations)
💡 This approach exists to build intuition by enumerating all permutations and picking the k-th. It is straightforward but inefficient, illustrating why direct computation is needed.

Intuition

Generate all permutations of [1..n] in lex order using backtracking, then return the k-th one.

Algorithm

  1. Initialize a list to store all permutations.
  2. Use backtracking to generate all permutations of [1..n].
  3. Stop when the k-th permutation is generated.
  4. Return the k-th permutation as a string.
💡 The challenge is understanding how backtracking generates permutations and why this approach is too slow for large n.
</>
Code
def getPermutation(n, k):
    res = []
    used = [False] * n
    path = []
    def backtrack():
        if len(path) == n:
            res.append(''.join(map(str, path)))
            return
        for i in range(n):
            if not used[i]:
                used[i] = True
                path.append(i + 1)
                backtrack()
                path.pop()
                used[i] = False
    backtrack()
    return res[k - 1]

# Driver code
if __name__ == '__main__':
    print(getPermutation(3, 3))  # Output: '213'
Line Notes
res = []Stores all permutations generated so far
used = [False] * nTracks which numbers are already in the current permutation path
def backtrack():Defines the recursive function to build permutations
if len(path) == n:Base case: a full permutation is formed
res.append(''.join(map(str, path)))Add the current permutation as a string to results
for i in range(n):Try each number from 1 to n if not used
used[i] = TrueMark number as used before recursion
path.append(i + 1)Add number to current permutation path
backtrack()Recurse to build deeper permutations
path.pop()Backtrack by removing last number
used[i] = FalseMark number as unused for other branches
return res[k - 1]Return the k-th permutation from the list
import java.util.*;
public class Solution {
    List<String> res = new ArrayList<>();
    boolean[] used;
    int n;
    public String getPermutation(int n, int k) {
        this.n = n;
        used = new boolean[n];
        backtrack(new StringBuilder());
        return res.get(k - 1);
    }
    private void backtrack(StringBuilder path) {
        if (path.length() == n) {
            res.add(path.toString());
            return;
        }
        for (int i = 0; i < n; i++) {
            if (!used[i]) {
                used[i] = true;
                path.append(i + 1);
                backtrack(path);
                path.deleteCharAt(path.length() - 1);
                used[i] = false;
            }
        }
    }
    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.getPermutation(3, 3)); // Output: 213
    }
}
Line Notes
List<String> res = new ArrayList<>()Stores all permutations generated
boolean[] usedTracks which numbers are used in current path
backtrack(new StringBuilder())Starts recursion with empty path
if (path.length() == n)Base case: full permutation formed
res.add(path.toString())Add current permutation to results
for (int i = 0; i < n; i++)Try each number from 1 to n
used[i] = trueMark number as used before recursion
path.append(i + 1)Add number to current path
backtrack(path)Recurse deeper
path.deleteCharAt(path.length() - 1)Backtrack by removing last number
used[i] = falseMark number as unused for other branches
return res.get(k - 1)Return the k-th permutation
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    vector<string> res;
    vector<bool> used;
    int n;
    void backtrack(string &path) {
        if ((int)path.size() == n) {
            res.push_back(path);
            return;
        }
        for (int i = 0; i < n; i++) {
            if (!used[i]) {
                used[i] = true;
                path.push_back('1' + i);
                backtrack(path);
                path.pop_back();
                used[i] = false;
            }
        }
    }
    string getPermutation(int n, int k) {
        this->n = n;
        used.assign(n, false);
        string path = "";
        backtrack(path);
        return res[k - 1];
    }
};

int main() {
    Solution sol;
    cout << sol.getPermutation(3, 3) << endl; // Output: 213
    return 0;
}
Line Notes
vector<string> resStores all generated permutations
vector<bool> usedTracks usage of numbers in current permutation
void backtrack(string &path)Recursive function to build permutations
if ((int)path.size() == n)Base case: full permutation formed
res.push_back(path)Add current permutation to results
for (int i = 0; i < n; i++)Try each number from 1 to n
used[i] = trueMark number as used before recursion
path.push_back('1' + i)Add number to current path
backtrack(path)Recurse deeper
path.pop_back()Backtrack by removing last number
used[i] = falseMark number as unused for other branches
return res[k - 1]Return the k-th permutation
function getPermutation(n, k) {
    const res = [];
    const used = new Array(n).fill(false);
    const path = [];
    function backtrack() {
        if (path.length === n) {
            res.push(path.join(''));
            return;
        }
        for (let i = 0; i < n; i++) {
            if (!used[i]) {
                used[i] = true;
                path.push(i + 1);
                backtrack();
                path.pop();
                used[i] = false;
            }
        }
    }
    backtrack();
    return res[k - 1];
}

// Test
console.log(getPermutation(3, 3)); // Output: 213
Line Notes
const res = []Stores all permutations generated
const used = new Array(n).fill(false)Tracks which numbers are used in current path
function backtrack()Recursive function to build permutations
if (path.length === n)Base case: full permutation formed
res.push(path.join(''))Add current permutation as string to results
for (let i = 0; i < n; i++)Try each number from 1 to n
used[i] = trueMark number as used before recursion
path.push(i + 1)Add number to current path
backtrack()Recurse deeper
path.pop()Backtrack by removing last number
used[i] = falseMark number as unused for other branches
return res[k - 1]Return the k-th permutation
Complexity
TimeO(n! * n)
SpaceO(n! * n)

Generating all permutations takes n! time and each permutation is length n, so total O(n! * n).

💡 For n=9, n! is 362,880, which is huge and impractical to generate fully.
Interview Verdict: TLE

This approach times out for larger n because it generates all permutations instead of directly computing the k-th.

🧠
Backtracking with Early Stop
💡 This approach improves brute force by stopping recursion once the k-th permutation is found, saving some time but still not efficient enough for large n.

Intuition

Generate permutations in lex order, but stop recursion as soon as the k-th permutation is reached.

Algorithm

  1. Initialize a global counter to track how many permutations have been generated.
  2. Use backtracking to generate permutations in lex order.
  3. When the counter reaches k, record the current permutation and stop recursion.
  4. Return the recorded permutation.
💡 The key difficulty is managing the global count and stopping recursion early to save time.
</>
Code
def getPermutation(n, k):
    used = [False] * n
    path = []
    count = [0]
    result = ['']
    def backtrack():
        if len(path) == n:
            count[0] += 1
            if count[0] == k:
                result[0] = ''.join(map(str, path))
            return
        if result[0]:
            return
        for i in range(n):
            if not used[i]:
                used[i] = True
                path.append(i + 1)
                backtrack()
                path.pop()
                used[i] = False
    backtrack()
    return result[0]

# Driver code
if __name__ == '__main__':
    print(getPermutation(3, 3))  # Output: '213'
Line Notes
count = [0]Tracks how many permutations have been generated so far
result = ['']Stores the k-th permutation once found
if len(path) == n:Base case: full permutation formed
count[0] += 1Increment count when a permutation is completed
if count[0] == k:Check if current permutation is the k-th
result[0] = ''.join(map(str, path))Save the k-th permutation
if result[0]:Stop recursion early if k-th permutation found
for i in range(n):Try each number from 1 to n
used[i] = TrueMark number as used
path.append(i + 1)Add number to current path
backtrack()Recurse deeper
path.pop()Backtrack by removing last number
used[i] = FalseMark number as unused
import java.util.*;
public class Solution {
    List<String> res = new ArrayList<>();
    boolean[] used;
    int n, count = 0;
    String result = "";
    public String getPermutation(int n, int k) {
        this.n = n;
        used = new boolean[n];
        backtrack(new StringBuilder(), k);
        return result;
    }
    private void backtrack(StringBuilder path, int k) {
        if (path.length() == n) {
            count++;
            if (count == k) {
                result = path.toString();
            }
            return;
        }
        if (!result.isEmpty()) return;
        for (int i = 0; i < n; i++) {
            if (!used[i]) {
                used[i] = true;
                path.append(i + 1);
                backtrack(path, k);
                path.deleteCharAt(path.length() - 1);
                used[i] = false;
            }
        }
    }
    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.getPermutation(3, 3)); // Output: 213
    }
}
Line Notes
int count = 0Tracks how many permutations generated
String result = ""Stores the k-th permutation once found
if (path.length() == n)Base case: full permutation formed
count++Increment count when a permutation is completed
if (count == k)Check if current permutation is the k-th
result = path.toString()Save the k-th permutation
if (!result.isEmpty()) returnStop recursion early if k-th permutation found
for (int i = 0; i < n; i++)Try each number from 1 to n
used[i] = trueMark number as used
path.append(i + 1)Add number to current path
backtrack(path, k)Recurse deeper
path.deleteCharAt(path.length() - 1)Backtrack by removing last number
used[i] = falseMark number as unused
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    vector<bool> used;
    int n, count = 0;
    string result = "";
    void backtrack(string &path, int k) {
        if ((int)path.size() == n) {
            count++;
            if (count == k) {
                result = path;
            }
            return;
        }
        if (!result.empty()) return;
        for (int i = 0; i < n; i++) {
            if (!used[i]) {
                used[i] = true;
                path.push_back('1' + i);
                backtrack(path, k);
                path.pop_back();
                used[i] = false;
            }
        }
    }
    string getPermutation(int n, int k) {
        this->n = n;
        used.assign(n, false);
        string path = "";
        backtrack(path, k);
        return result;
    }
};

int main() {
    Solution sol;
    cout << sol.getPermutation(3, 3) << endl; // Output: 213
    return 0;
}
Line Notes
int count = 0Tracks how many permutations generated
string result = ""Stores the k-th permutation once found
if ((int)path.size() == n)Base case: full permutation formed
count++Increment count when a permutation is completed
if (count == k)Check if current permutation is the k-th
result = pathSave the k-th permutation
if (!result.empty()) returnStop recursion early if k-th permutation found
for (int i = 0; i < n; i++)Try each number from 1 to n
used[i] = trueMark number as used
path.push_back('1' + i)Add number to current path
backtrack(path, k)Recurse deeper
path.pop_back()Backtrack by removing last number
used[i] = falseMark number as unused
function getPermutation(n, k) {
    const used = new Array(n).fill(false);
    const path = [];
    let count = 0;
    let result = '';
    function backtrack() {
        if (path.length === n) {
            count++;
            if (count === k) {
                result = path.join('');
            }
            return;
        }
        if (result) return;
        for (let i = 0; i < n; i++) {
            if (!used[i]) {
                used[i] = true;
                path.push(i + 1);
                backtrack();
                path.pop();
                used[i] = false;
            }
        }
    }
    backtrack();
    return result;
}

// Test
console.log(getPermutation(3, 3)); // Output: 213
Line Notes
let count = 0Tracks how many permutations generated
let result = ''Stores the k-th permutation once found
if (path.length === n)Base case: full permutation formed
count++Increment count when a permutation is completed
if (count === k)Check if current permutation is the k-th
result = path.join('')Save the k-th permutation
if (result) returnStop recursion early if k-th permutation found
for (let i = 0; i < n; i++)Try each number from 1 to n
used[i] = trueMark number as used
path.push(i + 1)Add number to current path
backtrack()Recurse deeper
path.pop()Backtrack by removing last number
used[i] = falseMark number as unused
Complexity
TimeO(k * n)
SpaceO(n)

In worst case, we generate up to k permutations, each of length n, so O(k * n).

💡 If k is large (close to n!), this is still very slow and impractical.
Interview Verdict: TLE for large k

Early stopping helps but still too slow for large inputs; motivates direct computation.

🧠
Optimal Direct Computation Using Factorial Number System
💡 This approach uses math to directly compute the k-th permutation without generating all permutations, making it efficient and scalable.

Intuition

Each position in the permutation can be determined by dividing k by factorials of remaining digits, selecting the appropriate number from the unused list.

Algorithm

  1. Precompute factorials from 0! to (n-1)!.
  2. Create a list of numbers from 1 to n.
  3. Adjust k to zero-based index (k = k - 1).
  4. For each position, determine the index by dividing k by factorial of remaining positions.
  5. Append the number at that index to the result and remove it from the list.
  6. Update k to k modulo factorial of remaining positions.
  7. Return the concatenated result string.
💡 The key insight is mapping k to indices using factorial blocks, which avoids generating permutations.
</>
Code
def getPermutation(n, k):
    factorial = [1] * n
    for i in range(1, n):
        factorial[i] = factorial[i - 1] * i
    numbers = list(range(1, n + 1))
    k -= 1  # zero-based index
    result = []
    for i in range(n - 1, -1, -1):
        idx = k // factorial[i]
        k %= factorial[i]
        result.append(str(numbers[idx]))
        numbers.pop(idx)
    return ''.join(result)

# Driver code
if __name__ == '__main__':
    print(getPermutation(3, 3))  # Output: '213'
Line Notes
factorial = [1] * nInitialize factorial array to store factorials up to (n-1)!
for i in range(1, n)Compute factorial values iteratively
numbers = list(range(1, n + 1))List of available numbers to pick from
k -= 1Convert k to zero-based index for calculation
for i in range(n - 1, -1, -1)Iterate from highest factorial to lowest
idx = k // factorial[i]Determine index of number to pick based on k
k %= factorial[i]Update k to remainder for next iteration
result.append(str(numbers[idx]))Add selected number to result
numbers.pop(idx)Remove used number from list
import java.util.*;
public class Solution {
    public String getPermutation(int n, int k) {
        int[] factorial = new int[n];
        factorial[0] = 1;
        for (int i = 1; i < n; i++) {
            factorial[i] = factorial[i - 1] * i;
        }
        List<Integer> numbers = new ArrayList<>();
        for (int i = 1; i <= n; i++) {
            numbers.add(i);
        }
        k--;
        StringBuilder result = new StringBuilder();
        for (int i = n - 1; i >= 0; i--) {
            int idx = k / factorial[i];
            k %= factorial[i];
            result.append(numbers.get(idx));
            numbers.remove(idx);
        }
        return result.toString();
    }
    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.getPermutation(3, 3)); // Output: 213
    }
}
Line Notes
int[] factorial = new int[n]Array to store factorials up to (n-1)!
for (int i = 1; i < n; i++)Compute factorial values iteratively
List<Integer> numbers = new ArrayList<>()List of available numbers to pick from
k--Convert k to zero-based index
for (int i = n - 1; i >= 0; i--)Iterate from highest factorial to lowest
int idx = k / factorial[i]Determine index of number to pick
k %= factorial[i]Update k for next iteration
result.append(numbers.get(idx))Add selected number to result
numbers.remove(idx)Remove used number from list
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    string getPermutation(int n, int k) {
        vector<int> factorial(n, 1);
        for (int i = 1; i < n; i++) {
            factorial[i] = factorial[i - 1] * i;
        }
        vector<int> numbers;
        for (int i = 1; i <= n; i++) {
            numbers.push_back(i);
        }
        k--;
        string result;
        for (int i = n - 1; i >= 0; i--) {
            int idx = k / factorial[i];
            k %= factorial[i];
            result += to_string(numbers[idx]);
            numbers.erase(numbers.begin() + idx);
        }
        return result;
    }
};

int main() {
    Solution sol;
    cout << sol.getPermutation(3, 3) << endl; // Output: 213
    return 0;
}
Line Notes
vector<int> factorial(n, 1)Stores factorials up to (n-1)!
for (int i = 1; i < n; i++)Compute factorial values iteratively
vector<int> numbersList of available numbers to pick from
k--Convert k to zero-based index
for (int i = n - 1; i >= 0; i--)Iterate from highest factorial to lowest
int idx = k / factorial[i]Determine index of number to pick
k %= factorial[i]Update k for next iteration
result += to_string(numbers[idx])Add selected number to result
numbers.erase(numbers.begin() + idx)Remove used number from list
function getPermutation(n, k) {
    const factorial = new Array(n).fill(1);
    for (let i = 1; i < n; i++) {
        factorial[i] = factorial[i - 1] * i;
    }
    const numbers = [];
    for (let i = 1; i <= n; i++) {
        numbers.push(i);
    }
    k--;
    let result = '';
    for (let i = n - 1; i >= 0; i--) {
        const idx = Math.floor(k / factorial[i]);
        k %= factorial[i];
        result += numbers[idx];
        numbers.splice(idx, 1);
    }
    return result;
}

// Test
console.log(getPermutation(3, 3)); // Output: 213
Line Notes
const factorial = new Array(n).fill(1)Array to store factorials up to (n-1)!
for (let i = 1; i < n; i++)Compute factorial values iteratively
const numbers = []List of available numbers to pick from
k--Convert k to zero-based index
for (let i = n - 1; i >= 0; i--)Iterate from highest factorial to lowest
const idx = Math.floor(k / factorial[i])Determine index of number to pick
k %= factorial[i]Update k for next iteration
result += numbers[idx]Add selected number to result
numbers.splice(idx, 1)Remove used number from list
Complexity
TimeO(n^2)
SpaceO(n)

Computing factorials is O(n), and removing elements from the list is O(n) per removal, total O(n^2).

💡 For n=9, this is very efficient compared to brute force, running in milliseconds.
Interview Verdict: Accepted

This is the optimal approach for this problem and is expected in interviews.

📊
All Approaches - One-Glance Tradeoffs
💡 In 95% of interviews, code the optimal direct computation approach after briefly mentioning brute force.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(n! * n)O(n! * n)Yes (deep recursion)YesMention only - never code
2. Backtracking with Early StopO(k * n)O(n)YesYesMention as improvement, but not optimal
3. Optimal Direct ComputationO(n^2)O(n)NoN/ACode this approach
💼
Interview Strategy
💡 Use this guide to understand the problem deeply, practice coding all approaches, and learn how to explain your thought process clearly in interviews.

How to Present

Step 1: Clarify the problem and constraints with the interviewer.Step 2: Explain the brute force approach to show understanding.Step 3: Discuss its inefficiency and propose early stopping.Step 4: Introduce the factorial number system for direct computation.Step 5: Code the optimal solution and test with examples.

Time Allocation

Clarify: 2min → Approach discussion: 5min → Code: 10min → Testing & optimization: 3min. Total ~20min

What the Interviewer Tests

The interviewer tests your understanding of permutations, ability to optimize brute force, and knowledge of factorial math for direct computation.

Common Follow-ups

  • What if numbers are not 1 to n but an arbitrary list? → Sort and apply same logic.
  • How to handle duplicates in the input? → Use backtracking with pruning or count permutations carefully.
💡 These follow-ups test your ability to generalize and handle variations of the problem.
🔍
Pattern Recognition

When to Use

1) Asked for k-th permutation or sequence; 2) Input size too large for brute force; 3) Lexicographical order mentioned; 4) Factorials or counting permutations involved.

Signature Phrases

k-th permutationlexicographical ordersequence number

NOT This Pattern When

Generating all permutations without order or combinations problems

Similar Problems

Next Permutation - find next lexicographical permutationPermutation II - handle duplicates in permutations

Practice

(1/5)
1. What is the time complexity of the optimal iterative approach using a queue to generate all letter combinations for a digit string of length n, assuming each digit maps to at most 4 letters?
medium
A. O(n * 4^n) because we process each digit and expand combinations exponentially
B. O(2^n) because each digit doubles the number of combinations
C. O(4^n * n) because there are up to 4^n combinations and each combination is built with n concatenations
D. O(n^2) because we process n digits and each combination takes n steps

Solution

  1. Step 1: Identify number of combinations

    Each digit maps to up to 4 letters, so total combinations are up to 4^n.
  2. Step 2: Analyze work per combination

    Each combination is built by concatenating letters for n digits, so each combination requires O(n) time to build.
  3. Step 3: Simplify complexity expression

    O(4^n * n) and O(n * 4^n) are equivalent; however, O(n * 4^n) because we process each digit and expand combinations exponentially is the standard notation emphasizing processing each digit and expanding combinations exponentially.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Time is exponential in n with linear cost per combination [OK]
Hint: Exponential combinations times linear build cost [OK]
Common Mistakes:
  • Confusing exponential base (2 vs 4)
  • Ignoring concatenation cost per combination
2. Consider the following code snippet for palindrome partitioning. Which line contains a subtle bug that causes incorrect or duplicate partitions?
def partition(s):
    def is_palindrome(left, right):
        while left < right:
            if s[left] != s[right]:
                return False
            left += 1
            right -= 1
        return True

    def backtrack(start, path):
        if start == len(s):
            result.append(path)  # Line X
            return
        for end in range(start, len(s)):
            if is_palindrome(start, end):
                path.append(s[start:end+1])
                backtrack(end+1, path)
                path.pop()

    result = []
    backtrack(0, [])
    return result
medium
A. Line 6: palindrome check misses single character substrings
B. Line X: appending path without copying causes shared references
C. Line 12: for loop range should be from start+1 to len(s)+1
D. Line 15: missing path.pop() after backtrack call

Solution

  1. Step 1: Identify how results are stored

    Appending path directly stores a reference, so later modifications affect all stored results.
  2. Step 2: Correct approach

    Use result.append(path[:]) to append a copy, preserving each partition snapshot.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Shared references cause incorrect final partitions [OK]
Hint: Always append a copy of path to results [OK]
Common Mistakes:
  • Forgetting to copy path before appending
  • Incorrect palindrome check boundaries
  • Forgetting to pop after recursion
3. The following code attempts to generate all permutations of a list using Heap's algorithm but contains a subtle bug:
def permute(nums):
    res = [nums[:]]
    c = [0] * len(nums)
    i = 0
    while i < len(nums):
        if c[i] < i:
            if i % 2 == 0:
                nums[0], nums[i] = nums[i], nums[0]
            else:
                nums[c[i]], nums[i] = nums[i], nums[c[i]]
            res.append(nums)
            c[i] += 1
            i = 0
        else:
            c[i] = 0
            i += 1
    return res
What is the bug in this code?
medium
A. The used array c is not reset properly, causing infinite loop
B. Appending nums directly without copying causes all entries in res to reference the same list
C. Swapping nums[0] and nums[i] when i is even is incorrect logic
D. The initial res list should be empty, not contain nums[:] at start

Solution

  1. Step 1: Identify how results are stored

    The code appends nums directly to res without copying.
  2. Step 2: Understand consequences of appending references

    Since nums is mutated in-place, all entries in res point to the same list, resulting in duplicates of the final permutation.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Appending a copy nums[:] fixes the bug [OK]
Hint: Always append a copy of mutable lists to results [OK]
Common Mistakes:
  • Forgetting to copy before appending results in duplicate references
4. Examine the following snippet from an optimized Sudoku solver. Which line contains a subtle bug that can cause incorrect solutions or failure to backtrack properly?
def backtrack():
    if not empties:
        return True
    empties.sort(key=lambda x: len(get_candidates(x[0], x[1])))
    r, c = empties[0]
    for ch in get_candidates(r, c):
        board[r][c] = ch
        rows[r].add(ch)
        cols[c].add(ch)
        boxes[(r//3)*3 + c//3].add(ch)
        empties.pop(0)
        if backtrack():
            return True
        # Undo changes
        board[r][c] = '.'
        rows[r].remove(ch)
        cols[c].remove(ch)
        boxes[(r//3)*3 + c//3].remove(ch)
        empties.insert(0, (r, c))
    return False
medium
A. Line that pops the first empty cell from empties
B. Line that sorts empties by candidate count
C. Line that adds ch to rows[r]
D. Line that resets board[r][c] to '.' after backtracking

Solution

  1. Step 1: Identify mutation of empties list

    The line popping empties[0] removes the cell but is never restored on backtrack failure.
  2. Step 2: Consequence of missing restoration

    Without re-adding the cell to empties after failed recursion, future calls miss this cell, causing incorrect state and solutions.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Backtracking requires undoing all changes including empties list [OK]
Hint: Backtracking must undo all state changes, including empties list [OK]
Common Mistakes:
  • Forgetting to restore empties after pop
  • Assuming board reset is sufficient
5. What is the time complexity of the optimal backtracking with Trie approach for Word Search II, given a board of size MxN and a list of words with maximum length L? Assume the Trie is already built.
medium
A. O(M * N * 4 * 3^(L-1)) due to pruning and adjacency constraints
B. O(M * N * 4^L * W), where W is the number of words
C. O(M * N * L^2) because each cell explores all prefixes
D. O(M * N * L) since each cell is visited once per character

Solution

  1. Step 1: Identify branching factor in backtracking

    From each cell, up to 4 directions are possible initially, then up to 3 directions for subsequent steps due to no revisiting.
  2. Step 2: Calculate complexity with pruning

    Trie pruning reduces unnecessary paths, so complexity is roughly O(M * N * 4 * 3^(L-1)) where L is max word length.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Matches known complexity from Trie + backtracking analysis [OK]
Hint: Branching factor reduces after first step due to visited constraints [OK]
Common Mistakes:
  • Confusing W (number of words) as multiplicative factor in optimal approach.