Start learning free

LESSON 222 OF 409 · SORTING

Group Anagrams in C++ with O(n * log n * L * log L) Time Complexity

Practice O(n * log n * L * log L) Read the full explanation

Given a vector of strings, group the anagrams together. You can return the answer in any order.

An anagram is a word or phrase formed by rearranging the letters of a different word or phrase, using all the original letters exactly once.

Example:

Input:   strings = ["eat","tea","tan","ate","nat","bat"]

Output:  [["bat"],["nat","tan"],["ate","eat","tea"]]

Note:

For this lesson, your algorithm should run in O(n * log n * L * log L) time and use O(n * L) extra space, where n is the number of strings and L is the length of the longest string.
(There are faster solutions which we will discuss in future lessons)


STEP 1 OF 10
    Ctrl + Enter to check

    C++ · SORTING

    Group Anagrams in C++, explained

    Problem Definition

    The problem requires us to group an array of strings into anagrams. An anagram is a word formed by rearranging the letters of another word, using all the original letters exactly once.

    Input:

    An array of strings.

    Output:

    A list of lists, where each sublist contains strings that are anagrams of each other.

    Constraints:

    • All inputs are lowercase letters.
    • The length of each string is at most 100.

    Example:

    Input:   strings = ["eat","tea","tan","ate","nat","bat"]
    Output:  [["bat"],["nat","tan"],["ate","eat","tea"]]

    Understanding the Problem

    The core challenge is to identify and group strings that are anagrams. Anagrams have the same characters in the same frequency but in different orders. This problem is significant in text processing and can be applied in various fields such as cryptography, data analysis, and natural language processing.

    Potential Pitfalls:

    • Misidentifying non-anagrams as anagrams.
    • Handling large input sizes efficiently.

    Approach

    To solve this problem, we need to identify a way to group strings that are anagrams. A naive solution would involve comparing each string with every other string, which is inefficient.

    Naive Solution:

    Compare each string with every other string to check if they are anagrams. This approach has a time complexity of O(n^2 * L), where n is the number of strings and L is the average length of the strings. This is not optimal for large inputs.

    Optimized Solution:

    A more efficient approach involves sorting the characters of each string. Anagrams, when sorted, will result in the same string. We can use this property to group anagrams together.

    Steps:

    1. Create a hash map to store sorted strings as keys and lists of anagrams as values.
    2. For each string, sort its characters and use the sorted string as a key in the hash map.
    3. Append the original string to the list corresponding to the sorted string key.
    4. Return the values of the hash map as the result.

    Algorithm

    Here is a step-by-step breakdown of the algorithm:

    1. Initialize an empty hash map.
    2. Iterate over each string in the input array.
    3. Sort the characters of the string.
    4. Use the sorted string as a key in the hash map.
    5. Append the original string to the list corresponding to the sorted string key.
    6. After processing all strings, return the values of the hash map.

    Code Implementation

    #include <iostream>
    #include <vector>
    #include <string>
    #include <unordered_map>
    #include <algorithm>
    
    using namespace std;
    
    vector<vector<string>> groupAnagrams(vector<string>& strings) {
        unordered_map<string, vector<string>> anagramMap;
        
        // Iterate over each string in the input array
        for (const string& str : strings) {
            string sortedStr = str;
            // Sort the characters of the string
            sort(sortedStr.begin(), sortedStr.end());
            // Use the sorted string as a key in the hash map
            anagramMap[sortedStr].push_back(str);
        }
        
        // Prepare the result from the hash map values
        vector<vector<string>> result;
        for (const auto& entry : anagramMap) {
            result.push_back(entry.second);
        }
        
        return result;
    }
    
    int main() {
        vector<string> strings = {"eat", "tea", "tan", "ate", "nat", "bat"};
        vector<vector<string>> groupedAnagrams = groupAnagrams(strings);
        
        // Print the grouped anagrams
        for (const auto& group : groupedAnagrams) {
            for (const string& str : group) {
                cout << str << " ";
            }
            cout << endl;
        }
        
        return 0;
    }
    

    Complexity Analysis

    The time complexity of this approach is O(n * log n * L * log L), where n is the number of strings and L is the average length of the strings. Sorting each string takes O(L * log L) time, and we do this for each of the n strings. The space complexity is O(n * L) due to the storage of the hash map.

    Edge Cases

    Potential edge cases include:

    • Empty input array: The output should be an empty list.
    • Strings with different lengths: The algorithm should handle this naturally as sorting will still group anagrams correctly.

    Example Edge Cases:

    Input:   strings = []
    Output:  []
    Input:   strings = ["a"]
    Output:  [["a"]]

    Testing

    To test the solution comprehensively, consider the following test cases:

    • Simple cases with a few strings.
    • Cases with multiple anagram groups.
    • Edge cases such as empty input or single-character strings.

    Example Test Cases:

    Input:   strings = ["eat","tea","tan","ate","nat","bat"]
    Output:  [["bat"],["nat","tan"],["ate","eat","tea"]]
    Input:   strings = ["a"]
    Output:  [["a"]]

    Thinking and Problem-Solving Tips

    When approaching such problems, consider the following tips:

    • Understand the problem requirements and constraints thoroughly.
    • Think about properties of the problem that can simplify the solution (e.g., sorted anagrams).
    • Start with a naive solution to understand the problem better, then optimize.
    • Practice similar problems to improve problem-solving skills.

    Conclusion

    Grouping anagrams is a common problem that can be efficiently solved using sorting and hash maps. Understanding the properties of anagrams and leveraging data structures like hash maps can lead to optimal solutions. Practice and familiarity with such problems can significantly enhance problem-solving skills.

    Additional Resources

    For further reading and practice, consider the following resources:

    Keep going with C++About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free