Letter Case Permutation
Generate every case-variant of a string — DFS with two choices per letter.
3 min read
subsets backtracking string
Problem#
Given a string with letters and digits, return all strings obtained by independently toggling the case of each letter.
Examples#
"a1b2"→["a1b2","a1B2","A1b2","A1B2"]
Constraints#
1 <= s.length <= 12.
Approach#
DFS. At each position: if digit, recurse with one choice. If letter, recurse twice with each case.
Solution#
class Solution {public: vector<string> letterCasePermutation(string s) { vector<string> out; function<void(int)> dfs = [&](int i){ if (i == (int)s.size()) { out.push_back(s); return; } dfs(i + 1); if (isalpha((unsigned char)s[i])) { s[i] ^= 0x20; // toggle case (ASCII) dfs(i + 1); s[i] ^= 0x20; } }; dfs(0); return out; }};func letterCasePermutation(s string) []string { b := []byte(s) out := []string{} var dfs func(i int) dfs = func(i int) { if i == len(b) { out = append(out, string(b)) return } dfs(i + 1) if (b[i] >= 'a' && b[i] <= 'z') || (b[i] >= 'A' && b[i] <= 'Z') { b[i] ^= 0x20 dfs(i + 1) b[i] ^= 0x20 } } dfs(0) return out}from typing import List
class Solution: def letterCasePermutation(self, s: str) -> List[str]: b = list(s) out: List[str] = []
def dfs(i: int) -> None: if i == len(b): out.append(''.join(b)) return dfs(i + 1) if b[i].isalpha(): b[i] = b[i].swapcase() dfs(i + 1) b[i] = b[i].swapcase()
dfs(0) return outfunction letterCasePermutation(s) { const b = s.split(''); const out = []; const isAlpha = (c) => /[a-zA-Z]/.test(c); const swap = (c) => { const code = c.charCodeAt(0); return String.fromCharCode(code ^ 0x20); }; const dfs = (i) => { if (i === b.length) { out.push(b.join('')); return; } dfs(i + 1); if (isAlpha(b[i])) { b[i] = swap(b[i]); dfs(i + 1); b[i] = swap(b[i]); } }; dfs(0); return out;}class Solution { private char[] b; private List<String> out;
public List<String> letterCasePermutation(String s) { b = s.toCharArray(); out = new ArrayList<>(); dfs(0); return out; }
private void dfs(int i) { if (i == b.length) { out.add(new String(b)); return; } dfs(i + 1); if (Character.isLetter(b[i])) { b[i] ^= 0x20; dfs(i + 1); b[i] ^= 0x20; } }}function letterCasePermutation(s: string): string[] { const b = s.split(''); const out: string[] = []; const isAlpha = (c: string): boolean => /[a-zA-Z]/.test(c); const swap = (c: string): string => String.fromCharCode(c.charCodeAt(0) ^ 0x20); const dfs = (i: number): void => { if (i === b.length) { out.push(b.join('')); return; } dfs(i + 1); if (isAlpha(b[i])) { b[i] = swap(b[i]); dfs(i + 1); b[i] = swap(b[i]); } }; dfs(0); return out;}Editorial#
Each letter contributes a factor of 2 to the count. The ^= 0x20 trick toggles case in ASCII without branching on which case we’re in — uppercase and lowercase letters differ only in bit 5.
Complexity#
- Time: O(2^L * n) where L is letter count.
- Space: O(n).
Concept revision#
Related#