Effective Strategies to Conquer Anagrams, Group Anagrams, and Two Sum Challenges on LeetCode

Anagram:

An Anagram is a string that contains the same characters as another string, but the order can be different.

Example:

class Solution:
    def isAnagram(self, s:str, t:str) -> bool:
        return sorted(s) == sorted(t)

# Example usage:
s = "anagram"
t = "nagaram"
print(isAnagram(s, t))  # Output: True

s = "rat"
t = "car"
print(isAnagram(s, t))  # Output: False

The Code Explanation:
→ This Code checks and returns whether a string is an Anagram or not.
1. The code explains a function checking the string for anagrams.
2. When s is ‘anagram‘ and t is ‘nagaram‘, then we print the function with parameters. If the string value in s matches the string value in t, then it prints true.
3. If the values of both strings didn't match, it prints False.

  • Time complexity: O(nlog⁡n+mlog⁡m)O(nlogn+mlogm)

  • Space complexity: O(1) or O(n+m) depending on the sorting algorithm.

Grouping Anagrams:

Here, the array of strings that are anagrams is grouped with the corresponding strings in any order.

The Core Idea:
Anagrams are words made of the same letters, just in a different order. This means they will have the
exact same count of each letter*. We can use this letter count as a unique “signature“ or “key“ to group them.

Example:*
Given an array of strings strs, group the anagrams together. You can return the answer in any order.
*Input: *[“eat“, “tea“, “Tan“, “ate“, “nat“, “bat“]
*Expected Output: [[“bat“], [“nat“, “tan“], [“ate“, “eat“, “tea“]]

class Solution:
    def groupAnagrams(self,strs):
        res = defaultdict(list)
        for s in strs:
            count = [0] * 26 # a ... z
            for c in s:
                count[ord(c) - ord('a')] += 1
            res[tuple(count)].append(s)
        return list(res.values())
# Example usage:
strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
solution = Solution()
print(solution.groupAnagrams(strs))

Code Explanation:

Step 1: The Alphabet Blueprint - A list of 26 Zeros*We start by creating a template to count the letters from a to z. A list with 26 zeros is perfect for this. Each position (or index) in the list corresponds to a letter of the alphabet.

→ Index 0 represents the count of ‘a‘.
→ Index 1 represents the count of ‘b‘.
→ …and so on, up to index 25 for ‘z‘.

Step 2: Processing a Word and Updating Counts*Now, we’ll process a word, for example, “eat“. We go through it letter by letter and increase the count at the corresponding index in our list.

How do we find the index for a letter?
We use a simple formula: index = ord(character) - ord(‘a‘). The ord function gives us the ASCII value of a character.

→ For ‘e‘: ord(‘e‘) - ord(‘a‘) = 101 - 97 = 4. So, we increment the count at index 4.
→ for ‘a‘: ord(‘a‘) - ord(‘a‘) = 97 - 97 = 0. We increment the count at index 0.
→ for ‘t‘: ord(‘t‘) - ord(‘a‘) = 116- 97 = 19. We increment the count at index 19.

Step 3: Creating an Immutable Signature - Converting the list to a tuple, our count list [1, 0, 0, 0, 1, …, 1, …] is the signature for “eat”.

However, in Python, we want to use these signatures as keys in a dictionary to group the words. Lists are mutable (changeable) and cannot be used as dictionary keys.
So, we convert the list into a tuple. Tuples are immutable (unchangeable)
and cannot be used as dictionary keys.

Step 4: Grouping the words in a Dictionary
We use a dictionary (or a defaultdict) to store our groups. The tuple signature is the key, and the value is a list of words that match that signature.

Let us see how it works for a few words:
1.
***Word: “eat“*→ Generates Signature: (1, 0, …, 1, …, 1, …)
→ The dictionary is updated: { (signature) : [“eat“]}
2.
Word “tea*
→ It contains one ‘t‘, one ‘e‘, one ‘a‘.
→ It generates the \**EXACT SAME signature: (1, 0, …, 1, …, 1, …)*→ The Dictionary finds this key and appends “tea“: { (signature): [“eat“, “tea“]}
3.
Word: “bat“*
→ It contains one ‘b‘, one ‘a’, one ‘t‘.
→ It generated DIFFERENT signature (with counts ‘a‘, ‘b‘, ‘t‘).
→ A new entry is created in the dictionary: {…, (new_signature) : [“bat“]}*

  • Time complexity: O(m∗n)O(m∗n)

  • Space complexity:

    • O(m)O(m) extra space.

    • O(m∗n)O(m∗n) space for the output list

Two Sum

Two Sum is a basic problem asked in most of the MNCs in technical interviews.

What is the Two-Sum Problem?
The Two-Sum Problem asks you to find two numbers in a given array that their sum equals a specified target number. You must return their indices, not the numbers themselves.

Why is it important?
It helps beginners understand:
1. Hashmaps (Dictionaries)
2. Time Complexity optimization (from O(n²) to O(n))
3. Fundamental problem-solving strategies used in coding interviews(Google, Amazon, etc.)

When is Two Sum used?
It’s often used as a coding interview question to test your understanding of:
→ Arrays
→ Hashmaps
→ Algorithmic thinking
It also serves as a foundation for harder problems like 3Sum, 4Sum, and Subarray Sum problems.

Where is it applied in real life?
Conceptually similar ideas are applied in :
→ Financial apps: Finding a pair of expenses within budget.
→ Gaming: Pairing scores or items to reach a target.
→ Data Analysis: Finding two metrics that sum to a desired outcome.

Example code:

Given: An Array of Integers nums and an integer target.
Task: Find two numbers in the array such that they add up to the target, and return their indices.
You may assume:
1. There is exactly one Solution.
2. You cannot use the same element twice

class Solution:
    def twoSum(self, nums, target):
        prevMap = {} # value : index
           for i,n in enumerate(nums):
               diff = target - n
                if diff in prevMap:
                    return [prevMap[diff], i]
                prevMap[n] = i
            return 
# Example Usage:
nums = [2, 7, 11, 15]
target = 9
result = twoSum(nums, target)
print(result)  # Output: [0, 1]  (Because nums[0] + nums[1] = 2 + 7 = 9)

1. PrevMap is a Dictionary that Stores the number Value and its index. We have seen while Looping
Example,
If nums = [2, 7, 11 ,15]
prevMap would be like (2 : 0)
2.
***For i,n enumerate(nums)*Loops through the list nums
1. i is the index
2. n is the number at the index
3.
diff = target - n*
For the current number n,
What number do we need to reach the target?
1. Diff is the missing number we are looking for
Example,
→ If the target is 9 and n = 2,
then diff = 7 (because(2 + 7 = 9))*

4. It means we already saw a number earlier that fits this pair.
Return its index and current index.
i.e.,
nums = [2, 7]
target = 9
prevMap = {2 : 0}
diff (2) is in prevMap → return[0, 1]
prevMap[n] = 1
If diff not found, store n with its index i for future, and then return

Time Complexity : O(n)
Space Complexity : O(n)

0
Subscribe to my newsletter

Read articles from Sam Anirudh Malarvannan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sam Anirudh Malarvannan
Sam Anirudh Malarvannan