Bird
Raised Fist0
Interview Prepsubsets-combinationsmediumAmazonGoogleFacebook

Letter Case Permutation

Choose your preparation mode4 modes available
▶
Steps
setup

Initialize / Setup

The input string 'a1b2' is converted into a character array ['a', '1', 'b', '2']. The results list is initialized empty. The backtracking starts at index 0.

💡 Converting to a character array allows in-place modification of letters, which is efficient for exploring case permutations.
Line:res = [] arr = list(s) backtrack(0)
💡 Setup prepares the data structures and starts the recursive exploration from the first character.
📊
Letter Case Permutation - Watch the Algorithm Execute, Step by Step
Watching this step-by-step recursion tree is the fastest way to understand how backtracking explores all letter case combinations systematically and how the algorithm reverts changes to explore alternative paths.
Step 1/16
·Active fill★Answer cell
enteringi=0
Call Path (current → root)
backtrack(0)
Remaining
lowercase or uppercase for 'a'
enteringi=1
Call Path (current → root)
backtrack(1)
backtrack(0)
Remaining
digit skip at index 1
Partial: [a]
enteringi=2
Call Path (current → root)
backtrack(2)
backtrack(1)
backtrack(0)
Remaining
lowercase or uppercase for 'b'
Partial: [a, 1]
enteringi=3
Call Path (current → root)
backtrack(3)
backtrack(2)
backtrack(1)
backtrack(0)
Remaining
digit skip at index 3
Partial: [a, 1, b]
enteringi=4
Call Path (current → root)
backtrack(4)
backtrack(3)
backtrack(2)
backtrack(1)
backtrack(0)
Partial: [a, 1, b, 2]
backtrackingi=4
Call Path (current → root)
backtrack(4)
backtrack(3)
backtrack(2)
backtrack(1)
backtrack(0)
Partial: [a, 1, b, 2]
Found 1: [a1b2]
enteringi=4
Call Path (current → root)
backtrack(4)
backtrack(3)
backtrack(2)
backtrack(1)
backtrack(0)
Tried
lowercase
Remaining
uppercase
Partial: [a, 1, B, 2]
Found 1: [a1b2]
backtrackingi=4
Call Path (current → root)
backtrack(4)
backtrack(3)
backtrack(2)
backtrack(1)
backtrack(0)
Tried
lowercaseuppercase
Partial: [a, 1, B, 2]
Found 2: [a1b2] [a1B2]
enteringi=1
Call Path (current → root)
backtrack(1)
backtrack(0)
Tried
lowercase
Remaining
uppercase
Partial: [A]
Found 2: [a1b2] [a1B2]
enteringi=2
Call Path (current → root)
backtrack(2)
backtrack(1)
backtrack(0)
Remaining
lowercase or uppercase for 'b'
Partial: [A, 1]
Found 2: [a1b2] [a1B2]
enteringi=3
Call Path (current → root)
backtrack(3)
backtrack(2)
backtrack(1)
backtrack(0)
Remaining
digit skip at index 3
Partial: [A, 1, b]
Found 2: [a1b2] [a1B2]
enteringi=4
Call Path (current → root)
backtrack(4)
backtrack(3)
backtrack(2)
backtrack(1)
backtrack(0)
Partial: [A, 1, b, 2]
Found 2: [a1b2] [a1B2]
backtrackingi=4
Call Path (current → root)
backtrack(4)
backtrack(3)
backtrack(2)
backtrack(1)
backtrack(0)
Partial: [A, 1, b, 2]
Found 3: [a1B2] [A1b2]
enteringi=4
Call Path (current → root)
backtrack(4)
backtrack(3)
backtrack(2)
backtrack(1)
backtrack(0)
Tried
lowercase
Remaining
uppercase
Partial: [A, 1, B, 2]
Found 3: [a1B2] [A1b2]
backtrackingi=4
Call Path (current → root)
backtrack(4)
backtrack(3)
backtrack(2)
backtrack(1)
backtrack(0)
Tried
lowercaseuppercase
Partial: [A, 1, B, 2]
Found 4: [A1b2] [A1B2]
backtrackingi=0
Call Path (current → root)
backtrack(0)
Tried
lowercaseuppercase
Found 4: [A1b2] [A1B2]

Key Takeaways

✓ Backtracking explores all letter case permutations by branching at each letter and skipping digits.

This insight is hard to see from code alone because the recursion and branching are implicit; visualization makes the branching explicit.

✓ Digits do not increase the recursion tree's branching factor and are efficiently skipped.

Understanding how digits simplify the recursion helps grasp the algorithm's efficiency.

✓ Backtracking reverts changes to explore alternative branches cleanly, ensuring all permutations are generated without corrupting state.

The importance of reverting state is subtle in code but critical for correctness; visualization shows this clearly.

Practice

(1/5)
1. Consider the following Python code implementing combination sum with reuse allowed:
from typing import List

def combinationSum(candidates: List[int], target: int) -> List[List[int]]:
    candidates.sort()
    result = []
    def backtrack(index, path, target):
        if target == 0:
            result.append(path[:])
            return
        if index == len(candidates) or target < 0:
            return
        max_use = target // candidates[index]
        for count in range(max_use + 1):
            backtrack(index + 1, path + [candidates[index]] * count, target - candidates[index] * count)
    backtrack(0, [], target)
    return result

print(combinationSum([2,3,6,7], 7))
What is the exact output of this code?
easy
A. [[7]]
B. [[2, 2, 3], [7]]
C. [[2, 2, 3]]
D. [[7], [2, 2, 3]]

Solution

  1. Step 1: Trace backtrack calls for candidates=[2,3,6,7], target=7

    Sorted candidates: [2,3,6,7]. The function tries counts of 2 from 0 to 3 (max_use=3), then recurses on next index. Valid combinations found are [7] and [2,2,3].
  2. Step 2: Check order of results appended

    Backtracking explores index 0 with counts 0..3, then index 1, so [7] is found after [2,2,3]. The result list appends [7] first, then [2,2,3]. But since the code appends when target==0, the order is [[7], [2, 2, 3]].
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Output matches expected combinations with correct order [OK]
Hint: Order depends on recursion path, not sorted output [OK]
Common Mistakes:
  • Assuming output is sorted by combination length or lex order
  • Missing one valid combination
2. Consider the following Python function that counts subsets with sum K using a space-optimized DP approach. What is the output when called with arr = [1, 2, 3, 3] and K = 6?
def count_subsets_space_optimized(arr, K):
    dp = [0] * (K + 1)
    dp[0] = 1
    for num in arr:
        for j in range(K, num - 1, -1):
            dp[j] += dp[j - num]
    return dp[K]

print(count_subsets_space_optimized(arr, K))
easy
A. 5
B. 4
C. 3
D. 6

Solution

  1. Step 1: Initialize dp array

    dp = [1,0,0,0,0,0,0] since dp[0]=1 and K=6.
  2. Step 2: Process each number updating dp

    After processing 1: dp = [1,1,0,0,0,0,0] After 2: dp = [1,1,1,1,0,0,0] After first 3: dp = [1,1,1,2,1,1,1] After second 3: dp = [1,1,1,3,2,2,3]
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    dp[6] = 5 subsets sum to 6 [OK]
Hint: Trace dp updates backward to avoid double counting [OK]
Common Mistakes:
  • Forgetting to iterate backward in dp array
  • Off-by-one errors in dp indices
3. Consider the following Python code that generates all subsets of nums = [1, 2, 3]. What is the value of subset when mask = 5 during the iteration?
easy
A. [1, 3]
B. [2, 3]
C. [1, 2]
D. [3]

Solution

  1. Step 1: Convert mask=5 to binary

    5 in binary is 101, meaning bits 0 and 2 are set.
  2. Step 2: Map bits to indices in nums

    Bit 0 corresponds to nums[0]=1, bit 2 corresponds to nums[2]=3, so subset = [1, 3].
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Mask 5 selects elements at positions 0 and 2 -> [1, 3] [OK]
Hint: Mask bits correspond to included elements [OK]
Common Mistakes:
  • Off-by-one bit indexing
  • Confusing mask bits with element values
4. What is the time complexity of the optimal iterative approach for generating all unique subsets from an array of length n that may contain duplicates?
medium
A. O(n^2) because of nested loops over the array
B. O(2^n) since each element doubles the subsets
C. O(2^n * n) due to generating all subsets and copying them
D. O(n * 2^n) because sorting dominates the complexity

Solution

  1. Step 1: Identify main operations affecting complexity

    Sorting takes O(n log n), which is dominated by subset generation. The algorithm generates all subsets (2^n) and copies subsets of average length O(n).
  2. Step 2: Calculate total time complexity

    Generating all subsets is O(2^n), and copying each subset of length up to n leads to O(2^n * n) total time.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Copying subsets causes the extra factor n, not just 2^n [OK]
Hint: Copying subsets adds factor n to 2^n subsets [OK]
Common Mistakes:
  • Ignoring subset copying cost and saying O(2^n)
  • Confusing sorting cost as dominant
  • Mistaking nested loops as O(n^2)
5. Suppose the problem changes: now candidates can include negative numbers, and you still want all unique combinations summing to the target with reuse allowed. Which modification to the backtracking approach is necessary to handle this variant correctly?
hard
A. Remove pruning on target < 0 and add a visited set to detect cycles to avoid infinite recursion.
B. No change needed; the existing backtracking with pruning on target < 0 still works correctly.
C. Sort candidates in descending order and prune when target is less than the smallest candidate.
D. Use dynamic programming instead of backtracking because negative numbers break recursion.

Solution

  1. Step 1: Understand impact of negative candidates

    Negative numbers allow sums to decrease indefinitely, causing infinite recursion if pruning on target < 0 is used or missing.
  2. Step 2: Modify pruning and cycle detection

    Pruning on target < 0 is invalid because target can oscillate; must remove it and add a visited set or memoization to detect repeated states and avoid infinite loops.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Cycle detection is essential when negative numbers allow infinite recursion [OK]
Hint: Negative numbers require cycle detection to prevent infinite recursion [OK]
Common Mistakes:
  • Assuming pruning on target < 0 always applies
  • Ignoring infinite recursion caused by negatives
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard