Before attempting this problem, you should be comfortable with:
We need to select a subset of words to maximize total score, where each word consumes letters from a shared pool. This is a classic subset selection problem. For each word, we decide whether to include it (if we have enough letters) or skip it. We explore all valid combinations using recursion and backtracking, restoring the letter counts after each recursive call.
0.class Solution:
def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int:
def can_form_word(w, letter_cnt):
word_cnt = Counter(w)
for c in word_cnt:
if word_cnt[c] > letter_cnt[c]:
return False
return True
def get_score(w):
res = 0
for c in w:
res += score[ord(c) - ord('a')]
return res
letter_cnt = Counter(letters)
def backtrack(i):
if i == len(words):
return 0
res = backtrack(i + 1) # skip
if can_form_word(words[i], letter_cnt): # include (when possible)
for c in words[i]:
letter_cnt[c] -= 1
res = max(res, get_score(words[i]) + backtrack(i + 1))
for c in words[i]:
letter_cnt[c] += 1
return res
return backtrack(0)Where is the number of words, is the maximum length of a word, is the size of the array , and is the size of the array .
The basic backtracking approach recalculates letter frequencies and scores for each word during recursion. We can speed this up by precomputing the frequency array and score for each word upfront. This avoids redundant character-by-character processing and makes the validity check and score lookup constant time operations.
26 elements for each letter).26 comparisons).class Solution:
def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int:
letter_cnt = [0] * 26
for c in letters:
letter_cnt[ord(c) - ord('a')] += 1
n = len(words)
word_scores = [0] * n
word_freqs = [[0] * 26 for _ in range(n)]
for i, word in enumerate(words):
for c in word:
idx = ord(c) - ord('a')
word_freqs[i][idx] += 1
word_scores[i] += score[idx]
def backtrack(i):
if i == n:
return 0
res = backtrack(i + 1) # skip
can_include = all(word_freqs[i][j] <= letter_cnt[j] for j in range(26))
if can_include: # include (when possible)
for j in range(26):
letter_cnt[j] -= word_freqs[i][j]
res = max(res, word_scores[i] + backtrack(i + 1))
for j in range(26):
letter_cnt[j] += word_freqs[i][j]
return res
return backtrack(0)Where is the number of words, is the maximum length of a word, is the size of the array , and is the size of the array .
Instead of recursive backtracking, we can iterate through all possible subsets using bit manipulation. With n words, there are 2^n possible subsets, each representable as an integer where bit i indicates whether word i is included. For each subset (bitmask), we check if all included words can be formed simultaneously and calculate the total score.
0 to 2^n - 1:class Solution:
def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int:
letter_cnt = [0] * 26
for c in letters:
letter_cnt[ord(c) - ord('a')] += 1
n = len(words)
word_scores = [0] * n
word_freqs = [[0] * 26 for _ in range(n)]
for i, word in enumerate(words):
for c in word:
idx = ord(c) - ord('a')
word_freqs[i][idx] += 1
word_scores[i] += score[idx]
res = 0
for mask in range(1 << n):
cur_score = 0
cur_letter_cnt = letter_cnt[:]
valid = True
for i in range(n):
if mask & (1 << i):
for j in range(26):
if word_freqs[i][j] > cur_letter_cnt[j]:
valid = False
break
if not valid:
break
for j in range(26):
cur_letter_cnt[j] -= word_freqs[i][j]
cur_score += word_scores[i]
if valid:
res = max(res, cur_score)
return resWhere is the number of words, is the maximum length of a word, is the size of the array , and is the size of the array .
When using backtracking, you must restore the letter counts after exploring the branch where you include a word. Forgetting to add the letters back after the recursive call means subsequent branches operate with incorrect letter availability, leading to wrong results or missing valid combinations.
You should verify that a word can be formed with available letters before consuming those letters. If you subtract letters first and then discover the word is invalid, you've already corrupted the state. Always check availability first, then subtract if the word can be formed.
When a word contains duplicate characters (e.g., "aab"), you need the letter pool to have enough of each character. A common bug is checking if each character exists at least once, rather than checking if the count is sufficient. Use frequency arrays or counters that properly track multiple occurrences.
The score array maps each letter to its value (index 0 = 'a', index 1 = 'b', etc.). A word's score is the sum of its letters' scores, not a single lookup. Misinterpreting this mapping or forgetting to sum all letters in the word produces incorrect scores.
This problem requires exploring all 2^n subsets of words because including a high-scoring word might prevent including multiple other words that together score higher. Greedy approaches (e.g., always taking the highest-scoring valid word first) don't guarantee the optimal solution.