Maximum Score Words Formed by Letters

Updated on 10 June, 2025
Maximum Score Words Formed by Letters header image

Problem Statement

The problem revolves around calculating the maximum possible score by forming a set of valid words using provided letters under specific constraints. We are given:

  • words: A list of distinct candidate words we can form.
  • letters: A list of available letters that can be used to form words. Each letter can only be used once.
  • score: An array where each index corresponds to the English lowercase letters ('a' to 'z') and contains the score for using that letter in a word.

Each word in words can be formed by using the letters from letters without exceeding the availability count of each letter. Moreover, you cannot use a word multiple times in your set. The challenge is to assemble the set of words that yields the highest score. This score is the summation of individual letter scores for all the chosen words.

Examples

Example 1

Input:

words = ["dog","cat","dad","good"], letters = ["a","a","c","d","d","d","g","o","o"], score = [1,0,9,5,0,0,3,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0]

Output:

23

Explanation:

Score a=1, c=9, d=5, g=3, o=2
We can form "dad" (5+1+5 = 11) and "good" (3+2+2+5 = 12).
Total score = 11 + 12 = 23.

We cannot form all four words because of limited letters.
Choosing "dad" and "dog" would result in a total score of 21, which is lower than 23.

Example 2

Input:

words = ["xxxz","ax","bx","cx"], letters = ["z","a","b","c","x","x","x"], score = [4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,10]

Output:

27

Explanation:

Score a=4, b=4, c=4, x=5, z=10

We can form "ax" (4+5 = 9), "bx" (4+5 = 9), and "cx" (4+5 = 9).

Total score = 9 + 9 + 9 = 27.

We cannot form "xxxz" because we only have 3 x's but also need z, and using all x's for "xxxz" would prevent us from forming the 3 smaller words that yield a better score.

Example 3

Input:

words = ["vultrcode"], letters = ["l","e","t","c","o","d"], score = [0,0,1,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0]

Output:

0

Explanation:

We do not have enough letters to form "vultrcode".

The word "vultrcode" requires 'v', 'u', 'r', which are not present in the given list of letters.

Therefore, no word can be formed and the total score is 0.

Constraints

  • 1 <= words.length <= 14
  • 1 <= words[i].length <= 15
  • 1 <= letters.length <= 100
  • letters[i].length == 1
  • score.length == 26
  • 0 <= score[i] <= 10
  • words[i], letters[i] contains only lower case English letters.

Approach and Intuition

By parsing the problem, we can derive the following steps and intuition for solving it:

  1. Understand the Scoring Scheme:

    • Each letter in the English alphabet has a score as per the score array.
    • The score for a word is the sum of the scores of each letter the word contains.
  2. Constraints on Usage of Letters:

    • A letter from letters can be used only once across all words selected. This means the frequency of each letter's use across the formed words cannot exceed its frequency in the letters list.
  3. Maximizing Score with Subset and Frequency Constraints:

    • The essence of the problem is a combinatorial optimization problem where the goal is to choose the optimal subset of words that maximizes the overall score.
    • Use a recursive (backtracking) approach or dynamic programming to explore all combinations of words. Begin by considering including the current word in the set and then consider not including it. For each word, check if it can be constructed with the current remaining letters.
  4. Frequency Counting:

    • Calculate the frequency of each character in the letters list. This will serve as a reference for checking if a word can be formed with the remaining letters.
    • For each word being considered in the subset, decrease the character count temporarily and backtrack after the recursive call to ensure other potential combinations can be explored with an unchanged character count.
  5. Recursive Approach Details:

    • Start with an initial score of zero and an empty "used" list of letters. For each word, check if it can be formed with the remaining letters and update the score. Use recursion to explore depth-first for all combinations and backtrack to try different combinational paths.
  6. Edge Cases:

    • If there are more words that need a particular letter than are available in letters, such words cannot all be included in the result.
    • If a word contains a letter not present in letters, it cannot be formed and hence not considered.

Through these steps, evaluate all possible combinations of words you can form and track the maximum score achieved during the process. This approach is feasible given the constraints and ensures we explore all potential word sets.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    int calculateMaxScore(vector<string>& wordList, vector<char>& chars,
                          vector<int>& points) {
        int n = wordList.size();
        highestScore = 0;
        letterCount = vector<int>(26, 0);
        vector<int> letterSubset = vector<int>(26, 0);
        // Populating frequency of each character
        for (char ch : chars) {
            letterCount[ch - 'a']++;
        }
        compute(n - 1, wordList, points, letterSubset, 0);
        return highestScore;
    }

private:
    int highestScore;
    vector<int> letterCount;

    // Validates the constructed subset against original character count
    bool canFormWord(vector<int>& letterSubset) {
        for (int i = 0; i < 26; i++) {
            if (letterCount[i] < letterSubset[i]) {
                return false;
            }
        }
        return true;
    }

    void compute(int idx, vector<string>& wordList, vector<int>& points,
                 vector<int>& letterSubset, int currentScore) {
        if (idx == -1) {
            // Assign higher score if the current subset's score is greater
            highestScore = max(highestScore, currentScore);
            return;
        }
        // Continue without adding the current word
        compute(idx - 1, wordList, points, letterSubset, currentScore);
        // Try adding the word at index idx
        int len = wordList[idx].length();
        for (int j = 0; j < len; j++) {
            int ch = wordList[idx][j] - 'a';
            letterSubset[ch]++;
            currentScore += points[ch];
        }

        if (canFormWord(letterSubset)) {
            // If valid, move to the next word
            compute(idx - 1, wordList, points, letterSubset, currentScore);
        }
        // Undo addition of the word at index idx
        for (int j = 0; j < len; j++) {
            int ch = wordList[idx][j] - 'a';
            letterSubset[ch]--;
            currentScore -= points[ch];
        }
    }
};

In this solution summary, calculate the maximum score by forming words using letters with assigned points. The approach taken involves recursion and backtracking to explore all combinations of forming words.

  • Start with a base function calculateMaxScore which initializes the tracking variables and triggers recursive computations.
  • Use a vector letterCount to maintain the frequency of each character available.
  • A recursive function compute helps in exploring each word's inclusion in the final arrangement. This function considers two scenarios: including the current word in the calculation and excluding it.
  • Another helper function canFormWord checks if the current subset of words can be formed with the available characters by comparing the required character counts with those available.
  • Each recursion keeps track of the current score and updates the highest score whenever a valid set of words is found that accords a higher score than previously recorded scenarios.

Remember to handle your character indexing carefully, as manipulation of indices for character frequency calculations is sensitive to off-by-one errors. This solution efficiently considers each word and its impact on the available characters, backtracking as necessary to explore alternative arrangements and ultimately determine the maximum achievable score.

java
class WordGame {

    public int calculateMaxScore(String[] wordList, char[] availableLetters, int[] letterScore) {
        int totalWords = wordList.length;
        // Calculate available frequency of each letter
        letterCount = new int[26];
        for (char letter : availableLetters) {
            letterCount[letter - 'a']++;
        }
        highestScore = 0;
        evaluate(totalWords - 1, wordList, letterScore, new int[26], 0);
        return highestScore;
    }

    private boolean canFormWord(int[] wordLetters) {
        for (int i = 0; i < 26; i++) {
            if (letterCount[i] < wordLetters[i]) {
                return false;
            }
        }
        return true;
    }

    private int highestScore;
    private int[] letterCount;

    private void evaluate(
        int index,
        String[] wordList,
        int[] letterScore,
        int[] wordLetters,
        int currentScore
    ) {
        if (index == -1) {
            highestScore = Math.max(highestScore, currentScore);
            return;
        }
        // Proceed without adding current word
        evaluate(index - 1, wordList, letterScore, wordLetters, currentScore);
        // Adding the current word
        int wordLength = wordList[index].length();
        for (int i = 0; i < wordLength; i++) {
            int letterIndex = wordList[index].charAt(i) - 'a';
            wordLetters[letterIndex]++;
            currentScore += letterScore[letterIndex];
        }

        if (canFormWord(wordLetters)) {
            evaluate(index - 1, wordList, letterScore, wordLetters, currentScore);
        }
        // Undo addition of the current word
        for (int i = 0; i < wordLength; i++) {
            int letterIndex = wordList[index].charAt(i) - 'a';
            wordLetters[letterIndex]--;
            currentScore -= letterScore[letterIndex];
        }
    }
}

This Java program utilizes a backtracking approach to find the maximum score obtainable by forming words from a given set of letters with associated scores.

  • Define a class named WordGame containing methods to calculate the maximum possible score:

    • calculateMaxScore initializes variables and starts the recursive evaluation using the evaluate method.
    • Use an array letterCount to keep track of the frequency of each letter available.
    • highestScore keeps a running total of the highest score found.
  • The recursion is handled by evaluate, which:

    1. Checks if adding the current word (from the end of the list) increases the score.
    2. Adds the word to the score calculation only if it can be formed with the remaining letters (canFormWord).
    3. Recursively calls itself to consider the next word.
    4. Backtracks by removing the last considered word and adjusting the score and available letters accordingly.
  • The helper method canFormWord checks the feasibility of forming a word with the remaining letters.

By efficiently managing the available letters and keeping track of the maximum score through recursive calls and backtracking, the program systematically explores all possible combinations of words to yield the highest score. The recursion and backtracking ensure that all combinations are considered, and the optimal solution is identified.

python
class Solution:
    def maximumScore(
        self, wordList: List[str], availableChars: List[str], value: List[int]
    ) -> int:
        totalWords = len(wordList)
        self.highestScore = 0
        letterFreq = [0] * 26
        currentLetterSubset = [0] * 26
        for char in availableChars:
            letterFreq[ord(char) - ord('a')] += 1

        def canAddWord(currentLetterSubset):
            for charIndex in range(26):
                if letterFreq[charIndex] < currentLetterSubset[charIndex]:
                    return False
            return True

        def calculateScore(wordIdx, wordList, value, currentLetterSubset, currentScore):
            if wordIdx == -1:
                self.highestScore = max(self.highestScore, currentScore)
                return
            calculateScore(wordIdx - 1, wordList, value, currentLetterSubset, currentScore)
            wordLength = len(wordList[wordIdx])
            for idx in range(wordLength):
                ch = ord(wordList[wordIdx][idx]) - ord('a')
                currentLetterSubset[ch] += 1
                currentScore += value[ch]
            if canAddWord(currentLetterSubset):
                calculateScore(wordIdx - 1, wordList, value, currentLetterSubset, currentScore)
            for idx in range(wordLength):
                ch = ord(wordList[wordIdx][idx]) - ord('a')
                currentLetterSubset[ch] -= 1
                currentScore -= value[ch]

        calculateScore(totalWords - 1, wordList, value, currentLetterSubset, 0)
        return self.highestScore

The Python solution is designed to determine the maximum score that can be achieved by forming words from a given list of available characters, where each character has an associated score. This problem is tackled using a depth-first search (DFS) approach combined with backtracking.

Here are the critical elements and methods used in the code:

  • Initialization: The solution initializes variables to count the total number of words, track the highest score, and manage the frequency of each letter from 'a' to 'z' using a list initialized with zeros.

  • Character Frequency Calculation: Before diving into the DFS, the code first calculates how many times each letter appears in the availableChars list. This frequency will help ensure we do not use more of any letter than available.

  • DFS Method - calculateScore:

    • This recursive function tries every combination of using or not using each word in the list.
    • For each word, the function increments the count for each letter in currentLetterSubset and adds the corresponding values to the current score.
    • After adjusting the counts and score, the function checks if this word can be formed with the currently used subsets of letters using another helper function canAddWord.
  • Feasibility Check - canAddWord:

    • This method verifies whether a word can be formed with the current subset of used letters compared to the actual available letters.
  • Backtracking:

    • After exploring the option with the word included, it backtracks by reverting the changes to the currentLetterSubset and the score, then continues to explore other possibilities.

By incrementally trying each word and backtracking accordingly, the algorithm finds the combination of words that yields the highest possible score, returned eventually by the function.

This approach efficiently uses recursion and backtracking to solve the problem, ensuring that all combinations are explored while adhering to the constraint of available letters. The function ultimately returns the maximum score possible with the given set of words and characters.

Comments

No comments yet.