Anagrams¶
Problem Statement¶
Given two strings, determine if they are anagrams of each other. An anagram is a word or phrase formed by rearranging the letters of another.
Examples¶
Example 1:¶
Input: s = "anagram", t = "nagaram"
Output: true
Explanation: Both strings contain the same characters with the same frequencies.
Example 2:¶
Input: s = "rat", t = "car"
Output: false
Explanation: The strings contain different characters.
Example 3:¶
Input: s = "listen", t = "silent"
Output: true
Explanation: Both strings contain the same characters with the same frequencies.
Approaches¶
1. Sorting Approach¶
Sort both strings and compare them.
public class Anagrams {
public boolean isAnagram_Sort(String s, String t) {
if (s.length() != t.length()) return false;
char[] sChars = s.toCharArray();
char[] tChars = t.toCharArray();
Arrays.sort(sChars);
Arrays.sort(tChars);
return Arrays.equals(sChars, tChars);
}
}
Time Complexity: O(n log n) due to sorting Space Complexity: O(n) for creating char arrays
2. Character Count Array (Optimal for ASCII)¶
Use an array to count character frequencies.
public class Anagrams {
public boolean isAnagram_Count(String s, String t) {
if (s.length() != t.length()) return false;
int[] charCount = new int[26]; // For lowercase letters
for (int i = 0; i < s.length(); i++) {
charCount[s.charAt(i) - 'a']++;
charCount[t.charAt(i) - 'a']--;
}
for (int count : charCount) {
if (count != 0) return false;
}
return true;
}
}
Time Complexity: O(n) Space Complexity: O(1) - fixed size array
3. HashMap Approach (For Unicode)¶
Use a HashMap to count character frequencies.
public class Anagrams {
public boolean isAnagram_HashMap(String s, String t) {
if (s.length() != t.length()) return false;
Map<Character, Integer> charCount = new HashMap<>();
// Count characters in s
for (char c : s.toCharArray()) {
charCount.put(c, charCount.getOrDefault(c, 0) + 1);
}
// Decrement counts for t
for (char c : t.toCharArray()) {
if (!charCount.containsKey(c)) return false;
int count = charCount.get(c);
if (count == 1) {
charCount.remove(c);
} else {
charCount.put(c, count - 1);
}
}
return charCount.isEmpty();
}
}
Time Complexity: O(n) Space Complexity: O(k) where k is the number of unique characters
Group Anagrams¶
A common variation is to group multiple strings into anagram groups.
public class Anagrams {
public List<List<String>> groupAnagrams(String[] strs) {
if (strs == null || strs.length == 0) return new ArrayList<>();
Map<String, List<String>> anagramGroups = new HashMap<>();
for (String str : strs) {
// Create character count key
char[] chars = new char[26];
for (char c : str.toCharArray()) {
chars[c - 'a']++;
}
String key = new String(chars);
// Add to appropriate group
anagramGroups.computeIfAbsent(key, k -> new ArrayList<>()).add(str);
}
return new ArrayList<>(anagramGroups.values());
}
}
Complete Solution with Tests¶
public class AnagramsTest {
public static void main(String[] args) {
Anagrams solution = new Anagrams();
// Test cases for isAnagram
String[][] testPairs = {
{"anagram", "nagaram"},
{"rat", "car"},
{"listen", "silent"},
{"", ""},
{"a", "a"},
{"ab", "ba"}
};
for (String[] pair : testPairs) {
System.out.println("Testing: " + pair[0] + ", " + pair[1]);
System.out.println("Sort approach: " +
solution.isAnagram_Sort(pair[0], pair[1]));
System.out.println("Count approach: " +
solution.isAnagram_Count(pair[0], pair[1]));
System.out.println("HashMap approach: " +
solution.isAnagram_HashMap(pair[0], pair[1]));
System.out.println();
}
// Test groupAnagrams
String[] words = {"eat", "tea", "tan", "ate", "nat", "bat"};
System.out.println("Group Anagrams Result:");
System.out.println(solution.groupAnagrams(words));
}
}
Variations¶
1. Valid Anagram After One Character Removal¶
public boolean isAnagramAfterOneRemoval(String s, String t) {
if (Math.abs(s.length() - t.length()) != 1) return false;
String longer = s.length() > t.length() ? s : t;
String shorter = s.length() > t.length() ? t : s;
int[] charCount = new int[26];
for (char c : longer.toCharArray()) charCount[c - 'a']++;
for (char c : shorter.toCharArray()) charCount[c - 'a']--;
int diffCount = 0;
for (int count : charCount) {
if (count > 1) return false;
if (count == 1) diffCount++;
}
return diffCount == 1;
}
2. K-Anagrams (At most k different characters)¶
public boolean isKAnagram(String s, String t, int k) {
if (s.length() != t.length()) return false;
int[] charCount = new int[26];
for (char c : s.toCharArray()) charCount[c - 'a']++;
for (char c : t.toCharArray()) charCount[c - 'a']--;
int diff = 0;
for (int count : charCount) {
diff += Math.abs(count);
}
return diff <= 2 * k;
}
Common Pitfalls and Tips¶
- Length Check: Always check string lengths first.
- Case Sensitivity: Clarify if the comparison is case-sensitive.
- Space Characters: Define how to handle spaces and special characters.
- Empty Strings: Consider if empty strings are anagrams of each other.
- Unicode Support: Choose appropriate approach based on character set.
Interview Tips¶
- Start with clarifying questions about requirements.
- Discuss trade-offs between different approaches.
- Consider memory constraints when choosing an approach.
- Handle edge cases explicitly.
- Mention possible optimizations.
Follow-up Questions¶
- How would you handle Unicode characters?
- What if the strings are very long?
- How would you modify the solution for streaming input?
- Can you solve it with constant space?
- How would you handle very large sets of strings for grouping?
Real-world Applications¶
- Spell checkers
- Word games
- DNA sequence analysis
- Cryptography
- Text analysis and processing
Testing Edge Cases¶
// Test empty strings
assert solution.isAnagram_Count("", "");
// Test single character
assert solution.isAnagram_Count("a", "a");
// Test same characters, different lengths
assert !solution.isAnagram_Count("rat", "rats");
// Test case sensitivity
assert !solution.isAnagram_Count("Rat", "tar");
// Test with spaces
assert solution.isAnagram_Count("debit card", "bad credit");
// Test with special characters
assert solution.isAnagram_Count("a!b@c#", "c@b#a!");
Performance Comparison¶
Approach | Time Complexity | Space Complexity | Best For |
---|---|---|---|
Sorting | O(n log n) | O(n) | Simple implementation |
Count Array | O(n) | O(1) | ASCII characters |
HashMap | O(n) | O(k) | Unicode characters |
Optimization Tips¶
- Use count array for ASCII characters
- Early return on length mismatch
- Use single pass for character counting
- Consider bit manipulation for small character sets
- Use StringBuilder for string manipulation