🧠
Brute Force (Pure Recursion / Backtracking)
💡 This approach exists to build intuition on how to explore all possible choices by branching at each letter. It’s the foundation for understanding backtracking.
Intuition
At each character, if it's a letter, we branch into two recursive calls: one with lowercase and one with uppercase. If it's a digit, we only have one choice. We collect all results at the end.
Algorithm
- Start from index 0 and an empty path string.
- If current character is a digit, append it and recurse to next index.
- If current character is a letter, recurse twice: once with lowercase, once with uppercase appended.
- When index reaches string length, add the built path to results.
💡 The challenge is to remember to branch only on letters and to accumulate the path correctly through recursion.
from typing import List
def letterCasePermutation(s: str) -> List[str]:
res = []
def backtrack(i: int, path: List[str]):
if i == len(s):
res.append(''.join(path))
return
if s[i].isdigit():
path.append(s[i])
backtrack(i + 1, path)
path.pop()
else:
path.append(s[i].lower())
backtrack(i + 1, path)
path.pop()
path.append(s[i].upper())
backtrack(i + 1, path)
path.pop()
backtrack(0, [])
return res
# Driver code
if __name__ == '__main__':
print(letterCasePermutation("a1b2"))
Line Notes
def letterCasePermutation(s: str) -> List[str]:Defines the main function with input string and output list of strings to return all permutations.
def backtrack(i: int, path: List[str]):Helper recursive function tracking current index and the path built so far to explore all branches.
if i == len(s):Base case: when index reaches end of string, join path and add to results as one valid permutation.
if s[i].isdigit():Digits do not branch; append digit and recurse to next index to maintain correctness.
path.append(s[i].lower())Try lowercase branch for current letter to explore one variation.
path.pop()Backtrack by removing last character to restore state before exploring next branch.
path.append(s[i].upper())Try uppercase branch for current letter to explore the other variation.
import java.util.*;
public class LetterCasePermutation {
public static List<String> letterCasePermutation(String s) {
List<String> res = new ArrayList<>();
backtrack(s.toCharArray(), 0, new StringBuilder(), res);
return res;
}
private static void backtrack(char[] chars, int i, StringBuilder path, List<String> res) {
if (i == chars.length) {
res.add(path.toString());
return;
}
if (Character.isDigit(chars[i])) {
path.append(chars[i]);
backtrack(chars, i + 1, path, res);
path.deleteCharAt(path.length() - 1);
} else {
path.append(Character.toLowerCase(chars[i]));
backtrack(chars, i + 1, path, res);
path.deleteCharAt(path.length() - 1);
path.append(Character.toUpperCase(chars[i]));
backtrack(chars, i + 1, path, res);
path.deleteCharAt(path.length() - 1);
}
}
public static void main(String[] args) {
System.out.println(letterCasePermutation("a1b2"));
}
}
Line Notes
public static List<String> letterCasePermutation(String s)Main function signature returning list of all permutations.
backtrack(s.toCharArray(), 0, new StringBuilder(), res);Start recursion with character array, index 0, empty path builder, and result list.
if (i == chars.length)Base case: when index reaches end, add current path string to results.
if (Character.isDigit(chars[i]))Digits do not branch; append digit and recurse to next index.
path.append(Character.toLowerCase(chars[i]));Try lowercase branch for current letter to explore one variation.
path.deleteCharAt(path.length() - 1);Backtrack by removing last appended character to restore state.
path.append(Character.toUpperCase(chars[i]));Try uppercase branch for current letter to explore the other variation.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Solution {
public:
vector<string> letterCasePermutation(string s) {
vector<string> res;
string path;
backtrack(s, 0, path, res);
return res;
}
private:
void backtrack(const string& s, int i, string& path, vector<string>& res) {
if (i == s.size()) {
res.push_back(path);
return;
}
if (isdigit(s[i])) {
path.push_back(s[i]);
backtrack(s, i + 1, path, res);
path.pop_back();
} else {
path.push_back(tolower(s[i]));
backtrack(s, i + 1, path, res);
path.pop_back();
path.push_back(toupper(s[i]));
backtrack(s, i + 1, path, res);
path.pop_back();
}
}
};
int main() {
Solution sol;
vector<string> res = sol.letterCasePermutation("a1b2");
for (auto& str : res) cout << str << endl;
return 0;
}
Line Notes
vector<string> letterCasePermutation(string s)Main function returning vector of all permutations.
void backtrack(const string& s, int i, string& path, vector<string>& res)Recursive helper with current index and path string reference to build permutations.
if (i == s.size())Base case: when index reaches end, add current path to results.
if (isdigit(s[i]))Digits do not branch; append digit and recurse to next index.
path.push_back(tolower(s[i]));Try lowercase branch for current letter to explore one variation.
path.pop_back();Backtrack by removing last character to restore state.
path.push_back(toupper(s[i]));Try uppercase branch for current letter to explore the other variation.
function letterCasePermutation(s) {
const res = [];
function backtrack(i, path) {
if (i === s.length) {
res.push(path);
return;
}
const c = s[i];
if (/[0-9]/.test(c)) {
backtrack(i + 1, path + c);
} else {
backtrack(i + 1, path + c.toLowerCase());
backtrack(i + 1, path + c.toUpperCase());
}
}
backtrack(0, "");
return res;
}
// Driver code
console.log(letterCasePermutation("a1b2"));
Line Notes
function letterCasePermutation(s)Main function to generate all permutations from input string.
function backtrack(i, path)Recursive helper tracking current index and the path string built so far.
if (i === s.length)Base case: when index reaches end, add current path to results.
if (/[0-9]/.test(c))Digits do not branch; recurse with same character appended.
backtrack(i + 1, path + c.toLowerCase())Try lowercase branch for current letter to explore one variation.
backtrack(i + 1, path + c.toUpperCase())Try uppercase branch for current letter to explore the other variation.
TimeO(2^k * n) where k is number of letters and n is string length
SpaceO(n) recursion stack + O(2^k * n) output space
Each letter doubles the number of permutations, digits do not branch. We build strings of length n for each permutation.
💡 For example, if input has 3 letters, we get 2^3=8 permutations, each of length n, so about 8*n operations.
Interview Verdict: Accepted
This brute force approach is accepted for typical constraints and is the foundation for more optimized solutions.