Skip to content
Related Articles

Related Articles

Improve Article
Maximize cost of forming a set of words using given set of characters
  • Last Updated : 28 Apr, 2021

Given an array arr[] consisting of N strings, an array letter[] consisting of M lowercase characters, and an array score[] such that score[i] is the cost of ith English alphabets, the task is to find the maximum cost of any valid set of words formed by using the given letters such that each letter can be used at most once and each word arr[i] can be formed at most once.

Examples:

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:
The scores of ‘a’, ‘c’, ‘d’, ‘g’, and ‘o’ are 9, 5, 3, 2 respectively.
The score of the word “dad” = (5 + 1 + 5) = 11.
The score of the word “good” = (3 + 2 + 2 + 5) = 12.
Therefore, the total score = 11 + 12 = 23, which is maximum.

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

Approach: The given problem can be solved by using recursion. The idea is to generate all possible subsets of the given array arr[] and if there exist any subsets whose all the strings can be formed by the given letters then find the cost of that subsets by adding the cost of the letters used. Follow the steps below to solve the problem:



  • Maintain a frequency array, freq[] to store the frequencies of characters in the array, letters.
  • Call a recursive function helper() which takes start (initially 0), words[], freq[] and score[] arrays as parameters.
    • Handle the base condition when start = N, then return 0.
    • Otherwise, copy the contents of the array freq[] in another array freqCopy[].
    • Initialize currScore and wordScore as 0 to store the maximum score and the score obtained from the current word.
    • Update wordScore if all of its characters of the current word, i.e. word[start] are present in the array, freqCopy, and then update freqCopy.
    • Include the current word by adding wordScore to the value returned by recursively calling function helper() and passing start+1, words[], freqCopy[] and score[] arrays as parameters.
    • Exclude the current word by recursively calling function helper() and passing start+1, words[], freq[] and score[] arrays as parameters.
    • Store the maximum score out of the two in currScore and return its value.
  • After completing the above steps, print the value returned by the function.

Below is the implementation of the above approach:

C++




// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
 
// Utility function to find
// maximum cost of generating
// any possible subsets of strings
int helper(vector<string> words, int start,
    vector<int> letterCounts, vector<int> score)
{
   
    // Base Case
    if (start == words.size())
        return 0;
 
    // Stores the current cost
    int currScore = 0;
 
    // Stores the cost of
    // by the current word
    int wordScore = 0;
 
    // Create a copy of the
    // letterCounts array
    vector<int> nextCounts = letterCounts;
 
    // Traverse the current word
    for (int i = 0;
         i < words[start].size();
         ++i) {
 
        // Store the current index & check
        // if its frequency is 0 or not
        int idx = words[start][i] - 'a';
 
        if (nextCounts[idx] == 0) {
 
            // If true, then update
            // wordScore to -1
            wordScore = -1;
            break;
        }
 
        // Otherwise, add the cost of
        // the current index to wordScore
        wordScore += score[idx];
 
        // Decrease its frequency
        nextCounts[idx]--;
    }
 
    // If wordScore > 0, then
    // recursively call for next index
    if (wordScore > 0)
        currScore = helper(words,
                           start + 1,
                           nextCounts,
                           score)
                    + wordScore;
 
    // Find the cost by not
    // including current word
    currScore = max(
        currScore, helper(words, start + 1,
                          letterCounts,
                          score));
 
    // Return the maximum score
    return currScore;
}
 
// Function to find the maximum cost
// of any valid set of words formed
// by using the given letters
int maxScoreWords(vector<string> words, vector<char> letters,
    vector<int> score)
{
    // Stores frequency of characters
    vector<int> letterCounts(26,0);
 
    for (char letter : letters)
        letterCounts[letter - 'a']++;
 
    // Find the maximum cost
    return helper(words, 0,
                  letterCounts,
                  score);
}
 
// Driver Code
int main()
{
    // Given arrays
    vector<string> words = { "dog", "cat", "dad", "good" };
    vector<char> letters = { 'a', 'a', 'c', 'd', 'd','d', 'g', 'o', 'o' };
    vector<int> 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 };
 
    // Function Call
    cout<<(maxScoreWords(words, letters, score));
}
 
// This  code is contributed by mohit kumar 29.

Java




// Java program for the above approach
import java.io.*;
class GFG {
 
    // Function to find the maximum cost
    // of any valid set of words formed
    // by using the given letters
    public static int maxScoreWords(
        String[] words, char[] letters,
        int[] score)
    {
        // Stores frequency of characters
        int[] letterCounts = new int[26];
 
        for (char letter : letters)
            letterCounts[letter - 'a']++;
 
        // Find the maximum cost
        return helper(words, 0,
                      letterCounts,
                      score);
    }
 
    // Utility function to find
    // maximum cost of generating
    // any possible subsets of strings
    public static int helper(
        String[] words, int start,
        int[] letterCounts, int[] score)
    {
        // Base Case
        if (start == words.length)
            return 0;
 
        // Stores the current cost
        int currScore = 0;
 
        // Stores the cost of
        // by the current word
        int wordScore = 0;
 
        // Create a copy of the
        // letterCounts array
        int[] nextCounts
            = letterCounts.clone();
 
        // Traverse the current word
        for (int i = 0;
             i < words[start].length();
             ++i) {
 
            // Store the current index & check
            // if its frequency is 0 or not
            int idx = words[start].charAt(i) - 'a';
 
            if (nextCounts[idx] == 0) {
 
                // If true, then update
                // wordScore to -1
                wordScore = -1;
                break;
            }
 
            // Otherwise, add the cost of
            // the current index to wordScore
            wordScore += score[idx];
 
            // Decrease its frequency
            nextCounts[idx]--;
        }
 
        // If wordScore > 0, then
        // recursively call for next index
        if (wordScore > 0)
            currScore = helper(words,
                               start + 1,
                               nextCounts,
                               score)
                        + wordScore;
 
        // Find the cost by not
        // including current word
        currScore = Math.max(
            currScore, helper(words, start + 1,
                              letterCounts,
                              score));
 
        // Return the maximum score
        return currScore;
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        // Given arrays
        String words[] = { "dog", "cat", "dad", "good" };
        char letters[] = { 'a', 'a', 'c', 'd', 'd',
                           'd', 'g', 'o', 'o' };
        int 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 };
 
        // Function Call
        System.out.println(
            maxScoreWords(words, letters, score));
    }
}

C#




// C# program for the above approach
using System;
using System.Linq;
 
class GFG
{
 
    // Function to find the maximum cost
    // of any valid set of words formed
    // by using the given letters
    public static int maxScoreWords(
        string[] words, char[] letters,
        int[] score)
    {
       
        // Stores frequency of characters
        int[] letterCounts = new int[26];
 
        foreach (char letter in letters)
            letterCounts[letter - 'a']++;
 
        // Find the maximum cost
        return helper(words, 0,
                      letterCounts,
                      score);
    }
 
    // Utility function to find
    // maximum cost of generating
    // any possible subsets of strings
    public static int helper(
        string[] words, int start,
        int[] letterCounts, int[] score)
    {
       
        // Base Case
        if (start == words.Length)
            return 0;
 
        // Stores the current cost
        int currScore = 0;
 
        // Stores the cost of
        // by the current word
        int wordScore = 0;
 
        // Create a copy of the
        // letterCounts array
        int[] nextCounts
            = letterCounts.ToArray();
 
        // Traverse the current word
        for (int i = 0;
             i < words[start].Length;
             ++i) {
 
            // Store the current index & check
            // if its frequency is 0 or not
            int idx = words[start][i] - 'a';
 
            if (nextCounts[idx] == 0) {
 
                // If true, then update
                // wordScore to -1
                wordScore = -1;
                break;
            }
 
            // Otherwise, add the cost of
            // the current index to wordScore
            wordScore += score[idx];
 
            // Decrease its frequency
            nextCounts[idx]--;
        }
 
        // If wordScore > 0, then
        // recursively call for next index
        if (wordScore > 0)
            currScore = helper(words,
                               start + 1,
                               nextCounts,
                               score)
                        + wordScore;
 
        // Find the cost by not
        // including current word
        currScore = Math.Max(
            currScore, helper(words, start + 1,
                              letterCounts,
                              score));
 
        // Return the maximum score
        return currScore;
    }
 
    // Driver Code
    public static void Main(string[] args)
    {
       
        // Given arrays
        string []words = { "dog", "cat", "dad", "good" };
        char []letters = { 'a', 'a', 'c', 'd', 'd',
                           'd', 'g', 'o', 'o' };
        int []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 };
 
        // Function Call
        Console.WriteLine(
            maxScoreWords(words, letters, score));
    }
}
 
// This code is contributed by ukasp.
Output: 
23

 

Time Complexity: O(2N)
Auxiliary Space: O(26)

Efficient Approach: The above approach can also be optimized by observing the fact that the above problem has an Optimal Substructure and Overlapping Subproblem. Therefore, the idea is to memoize the results in each recursive call and used those stored states when the same recursive calls are performed. For storing each recursive call, use a HashMap.

Below is the implementation of the above approach:

Java




// Java program for the above approach
 
import java.io.*;
import java.util.*;
 
class GFG {
 
    // Function to find the maximum cost
    // of any valid set of words formed
    // by using the given letters
    public static int maxScoreWords(
        String[] words, char[] letters,
        int[] score)
    {
        // Stores frequency of characters
        int[] letterCounts = new int[26];
 
        for (char letter : letters)
            letterCounts[letter - 'a']++;
 
        // Function Call to find the
        // maximum cost
        return helper(words, 0, letterCounts,
                      score,
                      new HashMap<>());
    }
 
    // Utility function to find maximum
    // cost of generating any possible
    // subsets of strings
    public static int helper(
        String[] words, int start,
        int[] letterCounts, int[] score,
        Map<String, Integer> memo)
    {
        // Base Case
        if (start == words.length)
            return 0;
 
        // If the score for this call
        // is already computed,    then
        // return result from hashmap
        String key = getKey(letterCounts, start);
 
        if (memo.containsKey(key))
            return memo.get(key);
 
        // Store the current score
        int currScore = 0;
 
        // Stores the cost contributed
        // by the current word
        int wordScore = 0;
 
        // Create a copy of the
        // letterCounts array
        int[] nextCounts = letterCounts.clone();
 
        // Traverse the current word
        // i.e., words[start]
        for (int i = 0;
             i < words[start].length();
             ++i) {
 
            // Store the current index
            // & check if its frequency
            // is 0 or not
            int idx = words[start].charAt(i) - 'a';
 
            if (nextCounts[idx] == 0) {
 
                // If true, then update
                // wordScore to -1 and
                // break
                wordScore = -1;
                break;
            }
 
            // Otherwise, add the cost
            // of the current index to
            // wordScore and decrease
            // its frequency
            wordScore += score[idx];
            nextCounts[idx]--;
        }
 
        // If wordScore > 0, then
        // recursively call for the
        // next index
        if (wordScore > 0)
            currScore = helper(words, start + 1,
                               nextCounts,
                               score, memo)
                        + wordScore;
 
        // Find the cost by not including
        // the current word
        currScore = Math.max(
            currScore, helper(words, start + 1,
                              letterCounts, score,
                              memo));
 
        // Memoize the result
        memo.put(key, currScore);
 
        // Return the maximum score
        return currScore;
    }
 
    // Function to get the unique key
    // from the  letterCounts array
    // with their index
    public static String getKey(
        int[] letterCounts, int idx)
    {
        // Append the frequency of
        // each character
        StringBuilder sb = new StringBuilder();
 
        for (int i = 0; i < 26; ++i)
            sb.append(letterCounts[i]);
 
        // Append the index
        sb.append(', ');
        sb.append(idx);
 
        // Return the string
        return sb.toString();
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        String words[] = { "dog", "cat", "dad", "good" };
        char letters[] = { 'a', 'a', 'c', 'd', 'd',
                           'd', 'g', 'o', 'o' };
        int 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 };
 
        System.out.println(
            maxScoreWords(words, letters,
                          score));
    }
}
Output: 
23

 

Time Complexity: O(N*X), where X is the size of the largest string in words[] array.
Auxiliary Space: O(N)

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.  To complete your preparation from learning a language to DS Algo and many more,  please refer Complete Interview Preparation Course.

In case you wish to attend live classes with industry experts, please refer DSA Live Classes




My Personal Notes arrow_drop_up
Recommended Articles
Page :