Maximum Length of a Concatenated String with Unique Characters

Updated on 10 June, 2025
Maximum Length of a Concatenated String with Unique Characters header image

Problem Statement

In this problem, you are provided with an array of strings named arr. Each string within the array arr consists exclusively of lowercase English letters. Your main objective is to generate a string s through the concatenation of a subsequence of the strings in arr. This concatenated string s must contain unique characters, meaning no character is repeated within s. The task is to determine the maximum length possible for such a string s using any subsequence from arr.

To ensure a clear understanding, a subsequence refers to a sequence derived from the original sequence (in this case, the array arr), where certain elements may be omitted without disrupting the order of the remaining elements. The successful concatenation should focus on maximizing the number of unique characters.

Examples

Example 1

Input:

arr = ["un","iq","ue"]

Output:

4

Explanation:

All valid concatenations are:
- ""
- "un"
- "iq"
- "ue"
- "uniq" ("un" + "iq")
- "ique" ("iq" + "ue")
Maximum length is 4.

Example 2

Input:

arr = ["cha","r","act","ers"]

Output:

6

Explanation:

Valid concatenations like "chaers" ("cha" + "ers") and "acters" ("act" + "ers") give the maximum length of 6.

Example 3

Input:

arr = ["abcdefghijklmnopqrstuvwxyz"]

Output:

26

Explanation:

The only string in arr already contains all 26 unique characters.

Constraints

  • 1 <= arr.length <= 16
  • 1 <= arr[i].length <= 26
  • arr[i] contains only lowercase English letters.

Approach and Intuition

The primary challenge is to find the longest string formed by a subsequence of arr such that all characters in the resulting string are unique.

Given the constraints (with arr.length ≤ 16), we can afford to use a backtracking or bitmask approach to explore all possible combinations:

  1. Filter Out Invalid Strings Early: If any string in arr contains duplicate characters, it can't be used in a valid result. Remove it from consideration.

  2. Use Backtracking to Explore Combinations:

    • For each valid string, decide whether to include it in the current combination or not.
    • If including the string does not lead to repeated characters, update the current combination and continue.
    • Keep track of the maximum length of a valid unique-character string found.
  3. Track Used Characters Efficiently:

    • A common optimization is to represent character sets using bitmasks (integers) so that set union and conflict checks are fast.
    • Each character can be assigned to a bit in a 26-bit integer.
  4. Return the Maximum Length:

    • During recursion or iteration, always update the result with the maximum length of a valid concatenated string with all unique characters.

This combinatorial and bitmask-based strategy is well-suited given the small input size and helps find the correct and optimal solution efficiently.

Solutions

  • C++
  • Java
  • JavaScript
  • Python
cpp
class Solution {
public:
    int findMaxCombination(vector<string>& input) {        
        unordered_set<int> uniqueBitsets;
        for (auto& str : input) 
            convertToBitset(uniqueBitsets, str);
        
        vector<int> bitsetList(uniqueBitsets.begin(), uniqueBitsets.end());
        return exploreOptions(bitsetList, 0, 0);
    }
    
private:    
    void convertToBitset(unordered_set<int>& set, string& str) {        
        int bitRepresentation = 0;
        for (char& ch : str) {
            int mask = 1 << ch - 'a';
            if (bitRepresentation & mask)
                return;
            bitRepresentation |= mask;
        }
        
        set.insert(bitRepresentation + (str.length() << 26));
    }
    
    int exploreOptions(vector<int>& bitsetList, int index, int current) {
        int currentChars = current & ((1 << 26) - 1);
        int currentLength = current >> 26;
        int maxLengthFound = currentLength;
        
        for (int i = index; i < bitsetList.size(); i++) {
            int chars = bitsetList[i] & ((1 << 26) - 1);
            int length = bitsetList[i] >> 26;
            
            if (chars & currentChars)
                continue;
            
            int combined = chars | currentChars | ((length + currentLength) << 26);
            maxLengthFound = max(maxLengthFound, exploreOptions(bitsetList, i + 1, combined));
        }
        return maxLengthFound;
    }
};

The C++ solution provided tackles the problem of finding the maximum length of a concatenated string made up of unique characters from a given list of strings. Here's a brief overview of the approach:

  • Bit Manipulation for Uniqueness Check and Length Storage: Each string is converted into a bit representation using a bitmask. This bitmask uses each bit to indicate the presence of a character in the string ('a' to 'z'). An additional shift operation adds the length of the string into the higher bits of the bitmask, allowing both the unique character set and the length of the string to be stored in a single integer.

  • Unordered Set for Unique Bitsets: An unordered set stores these unique bit representations. This mechanism helps in ensuring that only unique combinations are considered, avoiding unnecessary repetitions.

  • Recursive Combination Exploration: A recursive function explores all possible combinations to find the maximal length. It starts from a given index in the list of bitsets and recursively combines subsequent bitsets if they have no characters in common (as determined by a bitwise AND operation).

  • Efficiency Considerations:

    • Bitwise operations are fast, making the check for overlapping characters efficient.
    • Using an unordered set to store unique bit representations helps in quickly filtering out duplicate or redundant strings.
  • Backtracking to Build Solutions: The recursive function with backtracking ensures that all potential combinations are explored effectively by combining bitsets only when they represent non-overlapping character sets.

Overall, this solution efficiently combines bit manipulation, recursion, and backtracking to tackle the challenge of string combination, ensuring optimal computation time and resource utilization. The use of bit manipulation particularly aids in handling the unique character condition neatly and quickly.

java
class Solution {
    public int findMaxCombinedLength(List<String> arr) {        
        Set<Integer> uniqueCharSets = new HashSet<>();
        for (String s : arr) 
            processWord(uniqueCharSets, s);
        
        int[] processedArray = new int[uniqueCharSets.size()];
        int index = 0;
        for (Integer value : uniqueCharSets)
            processedArray[index++] = value;
        return exploreCombinations(processedArray, 0, 0);
    }
    
    private void processWord(Set<Integer> uniqueCharSets, String s) {        
        int bitRepresentation = 0;
        for (char ch : s.toCharArray()) {            
            int bit = 1 << ch - 'a';
            if ((bitRepresentation & bit) > 0)
                return;
            bitRepresentation |= bit;
        }
        
        uniqueCharSets.add(bitRepresentation + (s.length() << 26));
    }
    
    private int exploreCombinations(int[] processedArray, int index, int current) {        
        int currentChars = current & ((1 << 26) - 1);
        int currentLength = current >> 26;
        int maxLength = currentLength;
        
        for (int i = index; i < processedArray.length; i++) {
            int upcomingChars = processedArray[i] & ((1 << 26) - 1);
            int upcomingLength = processedArray[i] >> 26;
            
            if ((upcomingChars & currentChars) == 0) {
                int combined = upcomingChars | currentChars + (upcomingLength + currentLength << 26);
                maxLength = Math.max(maxLength, exploreCombinations(processedArray, i + 1, combined));
            }
        }
        return maxLength;
    }
}

The Java solution provided resolves the problem of finding the maximum length of a concatenated string made from a list of strings, ensuring all characters in the concatenated string are unique. Start by defining the findMaxCombinedLength method, which prepares and processes the input list through other helper methods.

  • Initialize a HashSet to accumulate unique combinations of characters from the strings, each represented with binary operations to facilitate quick and space-efficient comparisons.
  • Iterate over each string from the input list, converting it into a bitmask representation and adding the result to the set. This is handled by the processWord method, which skips any string containing duplicate characters by leveraging bitwise operations.
  • Convert the set into an array and calculate the maximum length of a unique-character string combination using exploreCombinations. This method recursively checks every possible combination of the processed strings and updates the maximum length.

In the processWord method:

  • Convert each character of the string into a bit representation and include it in an integer using bitwise OR operations. If any character repeats, it is detected with a bitwise AND operation.
  • Combine the length of the string with its bit pattern to form an integer that encapsulates both details (bit pattern in the lower 26 bits and length in the upper bits).

In the exploreCombinations method:

  • Use bitwise operations to extract character bits and length.
  • Recursively try to add each string into the current combination ensuring no character overlaps, achievable through bitwise AND checks.
  • Update the maximum length found with bitwise combinations of length and characters.

This approach ensures efficient and effective processing using bitwise manipulation to handle string combinations, guaranteeing optimal solution exploration with recursion.

js
var maxUniqueLength = function(words) {
    // Process words, convert them to bitsets, and store in a set
    let processedSet = new Set();
    for (let word of words)
        convertToBitSet(processedSet, word);

    // Convert the processed set to an array
    let processedArray = [...processedSet];
    return explore(processedArray, 0, 0);
};

var convertToBitSet = function(processedSet, word) {
    let bitRepresentation = 0;
    for (let char of word) {
        let bitMask = 1 << char.charCodeAt() - 97;
        if (bitRepresentation & bitMask)
            return;
        bitRepresentation += bitMask;
    }
    
    processedSet.add(bitRepresentation + (word.length << 26));
}

var explore = function(processedArray, index, currentResult) {
    let currentChars = currentResult & ((1 << 26) - 1);
    let currentLength = currentResult >> 26;
    let maxLength = currentLength;

    for (let i = index; i < processedArray.length; i++) {
        let entryChars = processedArray[i] & ((1 << 26) - 1);
        let entryLength = processedArray[i] >> 26;

        if (entryChars & currentChars)
            continue;

        let newResult = currentChars + entryChars + (currentLength + entryLength << 26);
        maxLength = Math.max(maxLength, explore(processedArray, i + 1, newResult));
    }
    return maxLength;
}

To solve the problem of finding the maximum length of a concatenated string with unique characters using Javascript, you can break down the solution into several key components:

  • First, process the initial array of words, converting each word into a bitwise representation that makes it easier to check for unique characters. This involves mapping each character to a specific bit in an integer. The function convertToBitSet() is responsible for this conversion. For each character in a word, a bit is set in an integer based on the character’s position in the alphabet (e.g., 'a' would map to the first bit). If any bit is found to be already set (indicating a repeated character), the word is skipped.

  • Store these integer representations in a set to avoid duplicates and represent only unique character sets. This utilizes Javascript's Set object. Only words represented by unique sets of bits are added along with their lengths shifted 26 bits left. The left shift operation helps to store both the unique character indicator and the length of the word in a single integer.

  • The main evaluation happens in the explore() function. This recursive function tries to concatenate every possible combination of words, represented by their bit masks, and computes the collective length. It does this by iterating over each processed bitset ensuring that the current set has no overlapping bits with the one being considered for concatenation (verifying uniqueness).

  • The function uses recursion to attempt adding every word in the processed set to the current combination, each time checking if there are common bits between the current concatenation result and the candidate word. If there are common bits, the word is skipped to maintain the uniqueness of characters. If not, the candidate word is added to the running total, and a new length is calculated.

  • Throughout the recursion, the maximum length of unique character string formed is updated and eventually returned as the result.

By configuring the problem in this bitwise manner, the solution efficiently manages the constraints related to uniqueness and length calculation, resulting in an effective approach to finding the maximum length of the concatenated strings with all unique characters.

python
class Solution:
    def maxUniqueLength(self, arr: List[str]) -> int:
        unique_set = set()
        for word in arr:
            self.convertToBitset(unique_set, word)

        processed_arr = list(unique_set)
        return self.calculateMax(processed_arr, 0, 0)

    def convertToBitset(self, bitset_collection: Set[int], word: str) -> None:
        bitrepresentation = 0
        for char in word:
            char_bit = 1 << ord(char) - 97
            if bitrepresentation & char_bit:
                return
            bitrepresentation |= char_bit

        bitset_collection.add(bitrepresentation + (len(word) << 26))

    def calculateMax(self, bitsets: List[int], index: int, current: int) -> int:
        curr_bits = current & ((1 << 26) - 1)
        curr_length = current >> 26
        max_length = curr_length

        for j in range(index, len(bitsets)):
            bits = bitsets[j] & ((1 << 26) - 1)
            length = bitsets[j] >> 26
            if bits & curr_bits:
                continue

            combined = bits | curr_bits + (length + curr_length << 26)
            max_length = max(max_length, self.calculateMax(bitsets, j + 1, combined))
        return max_length

In this solution for the problem of finding the maximum length of a concatenated string with unique characters using Python, a bit manipulation technique is utilized to efficiently check for uniqueness of characters in the strings. The code defines a Solution class with a method maxUniqueLength that processes the input list of strings. Here’s a step-by-step approach utilized in the solution:

  1. Convert each word in the list into a bitwise representation that indicates the presence of characters using the convertToBitset method. Bit positions 0 to 25 in an integer (representing the alphabet 'a' to 'z') are utilized to denote the presence (1) or absence (0) of a character.

  2. Additionally, bits 26 and onward in the integer store the length of the word using a shift operation. This bit representation helps in rapid checks for unique characters and concatenation lengths without string operations.

  3. Create a set unique_set to store unique bitwise representations of the words which ensures all entries considered further are composed of unique characters even within the individual strings.

  4. Convert unique_set into processed_arr which is then used to calculate the maximum length of possible uniquely-charactered strings via the calculateMax method.

  5. The calculateMax method recursively explores possible combinations of words, keeping track of the current bitmap and the total length of uniquely-charactered strings formed so far. If a new word shares no common bits with the current bitmap (indicating no repeating characters), it attempts adding this to the combination to see if it results in a greater length.

  6. The method returns the maximum length found.

This approach efficiently uses bit manipulation to handle the uniqueness constraint and avoids the overhead of dealing with direct string operations and repeated character checks, vastly improving the speed and usability for large inputs.

Comments

No comments yet.