Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Move from brute-force thinking to an efficient approach using hash map strategy.
You have n tiles, where each tile has one letter tiles[i] printed on it.
Return the number of possible non-empty sequences of letters you can make using the letters printed on those tiles.
Example 1:
Input: tiles = "AAB" Output: 8 Explanation: The possible sequences are "A", "B", "AA", "AB", "BA", "AAB", "ABA", "BAA".
Example 2:
Input: tiles = "AAABBC" Output: 188
Example 3:
Input: tiles = "V" Output: 1
Constraints:
1 <= tiles.length <= 7tiles consists of uppercase English letters.Problem summary: You have n tiles, where each tile has one letter tiles[i] printed on it. Return the number of possible non-empty sequences of letters you can make using the letters printed on those tiles.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map · Backtracking
"AAB"
"AAABBC"
"V"
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1079: Letter Tile Possibilities
class Solution {
public int numTilePossibilities(String tiles) {
int[] cnt = new int[26];
for (char c : tiles.toCharArray()) {
++cnt[c - 'A'];
}
return dfs(cnt);
}
private int dfs(int[] cnt) {
int res = 0;
for (int i = 0; i < cnt.length; ++i) {
if (cnt[i] > 0) {
++res;
--cnt[i];
res += dfs(cnt);
++cnt[i];
}
}
return res;
}
}
// Accepted solution for LeetCode #1079: Letter Tile Possibilities
func numTilePossibilities(tiles string) int {
cnt := [26]int{}
for _, c := range tiles {
cnt[c-'A']++
}
var dfs func(cnt [26]int) int
dfs = func(cnt [26]int) (res int) {
for i, x := range cnt {
if x > 0 {
res++
cnt[i]--
res += dfs(cnt)
cnt[i]++
}
}
return
}
return dfs(cnt)
}
# Accepted solution for LeetCode #1079: Letter Tile Possibilities
class Solution:
def numTilePossibilities(self, tiles: str) -> int:
def dfs(cnt: Counter) -> int:
ans = 0
for i, x in cnt.items():
if x > 0:
ans += 1
cnt[i] -= 1
ans += dfs(cnt)
cnt[i] += 1
return ans
cnt = Counter(tiles)
return dfs(cnt)
// Accepted solution for LeetCode #1079: Letter Tile Possibilities
struct Solution;
impl Solution {
fn num_tile_possibilities(tiles: String) -> i32 {
let mut counts: Vec<usize> = vec![0; 26];
for c in tiles.chars() {
counts[(c as u8 - b'A') as usize] += 1;
}
let mut res = 0;
Self::dfs(&mut res, &mut counts);
res
}
fn dfs(sum: &mut i32, counts: &mut Vec<usize>) {
for i in 0..26 {
if counts[i] > 0 {
*sum += 1;
counts[i] -= 1;
Self::dfs(sum, counts);
counts[i] += 1;
}
}
}
}
#[test]
fn test() {
let tiles = "AAB".to_string();
let res = 8;
assert_eq!(Solution::num_tile_possibilities(tiles), res);
let tiles = "AAABBC".to_string();
let res = 188;
assert_eq!(Solution::num_tile_possibilities(tiles), res);
}
// Accepted solution for LeetCode #1079: Letter Tile Possibilities
function numTilePossibilities(tiles: string): number {
const cnt: number[] = new Array(26).fill(0);
for (const c of tiles) {
++cnt[c.charCodeAt(0) - 'A'.charCodeAt(0)];
}
const dfs = (cnt: number[]): number => {
let res = 0;
for (let i = 0; i < 26; ++i) {
if (cnt[i] > 0) {
++res;
--cnt[i];
res += dfs(cnt);
++cnt[i];
}
}
return res;
};
return dfs(cnt);
}
Use this to step through a reusable interview workflow for this problem.
Generate every possible combination without any filtering. At each of n positions we choose from up to n options, giving nⁿ total candidates. Each candidate takes O(n) to validate. No pruning means we waste time on clearly invalid partial solutions.
Backtracking explores a decision tree, but prunes branches that violate constraints early. Worst case is still factorial or exponential, but pruning dramatically reduces the constant factor in practice. Space is the recursion depth (usually O(n) for n-level decisions).
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Wrong move: Mutable state leaks between branches.
Usually fails on: Later branches inherit selections from earlier branches.
Fix: Always revert state changes immediately after recursive call.