Start learning free

LESSON 266 OF 410 · HASH TABLE

Group Anagrams II in Python (O(n * L * log L) Time Complexity)

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

Given a list 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:

Your algorithm should run in O(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.


STEP 1 OF 5
    Ctrl + Enter to check

    PYTHON · HASH TABLE

    Group Anagrams in Python, explained

    Understanding the Problem

    The core challenge of this problem is to identify and group words that are anagrams of each other. An anagram is a rearrangement of the letters of a word to form another word. For example, "eat" and "tea" are anagrams because they contain the same letters.

    This problem has practical applications in text analysis, cryptography, and data clustering.

    Potential pitfalls include not correctly identifying anagrams or inefficiently grouping them, leading to suboptimal performance.

    Approach

    To solve this problem, we need to group words that are anagrams. A naive solution might involve comparing each word with every other word, but this would be inefficient with a time complexity of O(n^2 * L). Instead, we can use a more optimized approach:

    Optimized Approach

    1. **Sort and Hash**: For each word, sort its characters and use the sorted word as a key in a hash map (dictionary). All anagrams will have the same sorted key, so they will be grouped together.

    2. **Count and Hash**: Instead of sorting, count the frequency of each character and use this count as a key. This avoids the O(L log L) sorting time but requires more space for the count array.

    Algorithm

    We'll use the "Sort and Hash" approach for its simplicity and efficiency:

    1. Initialize an empty dictionary to store groups of anagrams.
    2. For each word in the input list:
      • Sort the characters of the word.
      • Use the sorted word as a key in the dictionary.
      • Append the original word to the list corresponding to this key.
    3. Return the values of the dictionary as the grouped anagrams.

    Code Implementation

    def group_anagrams(strings):
        # Dictionary to hold the groups of anagrams
        anagrams = {}
        
        for word in strings:
            # Sort the word to form the key
            sorted_word = ''.join(sorted(word))
            
            # If the key is not in the dictionary, add it with an empty list
            if sorted_word not in anagrams:
                anagrams[sorted_word] = []
            
            # Append the original word to the list corresponding to the sorted key
            anagrams[sorted_word].append(word)
        
        # Return the values of the dictionary as the grouped anagrams
        return list(anagrams.values())
    
    # Example usage
    strings = ["eat", "tea", "tan", "ate", "nat", "bat"]
    print(group_anagrams(strings))
    

    Complexity Analysis

    The time complexity of this approach is O(n * L * log L), where n is the number of words and L is the maximum length of a word. This is because sorting each word takes O(L log L) time, and we do this for each of the n words.

    The space complexity is O(n * L) because we store each word in the dictionary.

    Edge Cases

    Consider the following edge cases:

    • Empty input list: The function should return an empty list.
    • Single word: The function should return a list containing a single list with that word.
    • All words are anagrams: The function should return a single list containing all words.

    Testing

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

    def test_group_anagrams():
        assert group_anagrams([]) == []
        assert group_anagrams(["a"]) == [["a"]]
        assert group_anagrams(["eat", "tea", "tan", "ate", "nat", "bat"]) == [["eat", "tea", "ate"], ["tan", "nat"], ["bat"]]
        assert group_anagrams([""]) == [[""]]
        assert group_anagrams(["a", "b", "c", "a"]) == [["a", "a"], ["b"], ["c"]]
    
    test_group_anagrams()
    

    Thinking and Problem-Solving Tips

    When approaching such problems, consider the following tips:

    • Break down the problem into smaller parts and solve each part step-by-step.
    • Think about different ways to represent the problem (e.g., sorting, counting).
    • Optimize your solution by considering time and space complexity.
    • Practice similar problems to improve your problem-solving skills.

    Conclusion

    Grouping anagrams is a common problem that can be efficiently solved using sorting and hashing techniques. Understanding and solving such problems helps improve your algorithmic thinking and coding skills.

    Keep practicing and exploring different approaches to become proficient in problem-solving.

    Additional Resources

    For further reading and practice, consider the following resources:

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