Skip to content

Valid Anagram ​

Problem Statement ​

Given two strings s and t, return true if t is an anagram of s, and false otherwise.

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

LeetCode: 242. Valid Anagram

Examples ​

Example 1:

text
Input: s = "anagram", t = "nagaram"
Output: true

Example 2:

text
Input: s = "rat", t = "car"
Output: false

Constraints ​

  • 1 <= s.length, t.length <= 5 * 10^4
  • s and t consist of lowercase English letters.

Hash Map Approach ​

Valid Anagram Visualization

Key Insight ​

Two strings are anagrams if they have the same character frequencies. Use a hash map to count characters.

Algorithm ​

  1. If lengths differ, return False
  2. Count character frequencies in both strings
  3. Compare the two frequency maps

Solution ​

python
from collections import Counter

def isAnagram(s: str, t: str) -> bool:
    """
    Check if t is an anagram of s.

    Time: O(n) - count characters
    Space: O(1) - at most 26 characters
    """
    if len(s) != len(t):
        return False

    return Counter(s) == Counter(t)
java
import java.util.*;

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) return false;

        int[] count = new int[26];

        for (char c : s.toCharArray()) count[c - 'a']++;
        for (char c : t.toCharArray()) count[c - 'a']--;

        for (int v : count) {
            if (v != 0) return false;
        }

        return true;
    }
}

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

  • Time: Counting characters in both strings is O(n); comparing counters is O(26) = O(1) for fixed alphabet
  • Space: Counters store at most 26 lowercase letter counts (constant for fixed character set)
python
# Manual counting approach
def isAnagramManual(s: str, t: str) -> bool:
    """Without using Counter."""
    if len(s) != len(t):
        return False

    count = {}

    # Count characters in s
    for char in s:
        count[char] = count.get(char, 0) + 1

    # Decrement for characters in t
    for char in t:
        if char not in count:
            return False
        count[char] -= 1
        if count[char] < 0:
            return False

    return True

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

  • Time: Two passes through strings with O(1) hash map operations
  • Space: Hash map stores at most 26 entries for lowercase letters
python
# Array-based counting (faster for lowercase letters only)
def isAnagramArray(s: str, t: str) -> bool:
    """
    Use fixed-size array for lowercase letters.

    Time: O(n)
    Space: O(1) - always 26 elements
    """
    if len(s) != len(t):
        return False

    count = [0] * 26

    for i in range(len(s)):
        count[ord(s[i]) - ord('a')] += 1
        count[ord(t[i]) - ord('a')] -= 1

    return all(c == 0 for c in count)

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

  • Time: Single pass incrementing/decrementing counts, then O(26) check for all zeros
  • Space: Fixed 26-element array regardless of input size

Alternative: Sorting Approach ​

python
def isAnagramSorting(s: str, t: str) -> bool:
    """
    Sort both strings and compare.

    Time: O(n log n)
    Space: O(n) - sorted copies
    """
    return sorted(s) == sorted(t)

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

  • Time: Sorting each string takes O(n log n); comparison is O(n)
  • Space: sorted() creates new lists containing all characters

Complexity Analysis ​

ApproachTimeSpace
Hash MapO(n)O(1)*
Array (26 chars)O(n)O(1)
SortingO(n log n)O(n)

*Space is O(1) for fixed character set (26 lowercase letters)

Edge Cases ​

  1. Empty strings: "", "" - Both empty, return True
  2. Different lengths: "a", "ab" - Return False immediately
  3. Single character: "a", "a" - Return True
  4. Same string: "test", "test" - Return True

Follow-up: Unicode Characters ​

If the inputs contain Unicode characters:

python
def isAnagramUnicode(s: str, t: str) -> bool:
    """
    Handle Unicode characters using hash map.

    Time: O(n)
    Space: O(k) where k is unique characters
    """
    if len(s) != len(t):
        return False

    char_count = {}

    for char in s:
        char_count[char] = char_count.get(char, 0) + 1

    for char in t:
        if char not in char_count or char_count[char] == 0:
            return False
        char_count[char] -= 1

    return True

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

  • Time: Two linear passes through strings with O(1) hash operations
  • Space: Hash map stores k unique characters (unbounded for Unicode)

Variations ​

Group Anagrams ​

Group strings that are anagrams of each other:

python
from collections import defaultdict
from typing import List

def groupAnagrams(strs: List[str]) -> List[List[str]]:
    """
    Group anagrams using sorted string as key.

    Time: O(n * k log k) where k is max string length
    Space: O(n * k)
    """
    groups = defaultdict(list)

    for s in strs:
        key = tuple(sorted(s))
        groups[key].append(s)

    return list(groups.values())

Complexity: Time O(n * k log k) · Space O(n * k)

  • Time: For each of n strings, sorting k characters takes O(k log k)
  • Space: Hash map stores all n strings; keys are tuples of length k
python
# Optimized: Use character count as key
def groupAnagramsOptimized(strs: List[str]) -> List[List[str]]:
    """
    Use character count tuple as key (avoids sorting).

    Time: O(n * k)
    Space: O(n * k)
    """
    groups = defaultdict(list)

    for s in strs:
        count = [0] * 26
        for char in s:
            count[ord(char) - ord('a')] += 1
        groups[tuple(count)].append(s)

    return list(groups.values())

Complexity: Time O(n * k) · Space O(n * k)

  • Time: For each of n strings, counting k characters is O(k) - no sorting needed
  • Space: Same storage requirements; keys are 26-element tuples (constant per string)

Find All Anagrams in a String ​

python
def findAnagrams(s: str, p: str) -> List[int]:
    """
    Find all start indices of p's anagrams in s.
    Uses sliding window.

    Time: O(n)
    Space: O(1)
    """
    if len(p) > len(s):
        return []

    result = []
    p_count = Counter(p)
    window = Counter(s[:len(p)])

    if window == p_count:
        result.append(0)

    for i in range(len(p), len(s)):
        # Add new character
        window[s[i]] += 1

        # Remove old character
        old_char = s[i - len(p)]
        window[old_char] -= 1
        if window[old_char] == 0:
            del window[old_char]

        if window == p_count:
            result.append(i - len(p) + 1)

    return result

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

  • Time: Single pass with O(1) window updates and O(26) counter comparisons
  • Space: Window and pattern counters store at most 26 characters each
Group Anagrams (LeetCode 49)

Problem: Group strings that are anagrams of each other.

Key Insight: Anagrams have same sorted characters or same character counts. Use as hash key.

Approach: Create hash key from sorted string or character count tuple. Group strings by key.

Complexity: Time O(n * k log k) for sorting approach, Space O(n * k)

Find All Anagrams in a String (LeetCode 438)

Problem: Find all start indices of p's anagrams in s.

Key Insight: Use sliding window of size p.length. Compare character counts.

Approach: Fixed-size sliding window with frequency comparison. Slide and check at each position.

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

Permutation in String (LeetCode 567)

Problem: Check if s2 contains any permutation of s1.

Key Insight: Same as finding one anagram. Use fixed-size sliding window.

Approach: Maintain frequency map for s1. Slide window over s2, return true if frequencies match.

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

Ransom Note (LeetCode 383)

Problem: Return true if ransomNote can be constructed from magazine letters.

Key Insight: Count characters in magazine, check if enough for ransomNote.

Approach: Build frequency map from magazine. Decrement for each ransomNote char, fail if negative.

Complexity: Time O(n + m), Space O(1)