This problem may come up during an interview, and it’s a bit of fun. How do we determine if two strings are anagrams?

If you’re practicing for an interview, write your solution in a text editor rather than an IDE. For syntax coloring, Visual Studio Code without IntelliSense works well with support for many languages. I’ve provided my solution in C# (csharp) and Java followed by Objective-C (ObjC) and Swift at the bottom.

observations

By definition, an anagram is a word or phrase formed from another word or phrase by rearranging the letters. Spaces and punctuation don’t count, just the letters ignoring case. Here are a few samples:

  • “Tom Marvolo Riddle” <-> “I am Lord Voldemort!”
  • “Dave Barry” <-> “Ray Adverb”
  • “debit card” <-> “bad credit”
  • “astronomer” <-> “Moon starer”
  • “School master” <-> “the classroom”

Since we must use all of the letters we can conclude that ignoring spaces and punctuation, the two strings must be the same length. Also, the same phrase is not an anagram of itself.

Notice that if we sort the letters of a phrase and its anagram we get identical strings.

assumptions

Let’s assume gibberish is acceptable. We want to allow proper names, and a person could create a secret password from an actual phrase. With this assumption we won’t need to look up words in a dictionary, and we can test secret passwords and phrases.

Some test strings that should fail:

  • “banana” and “bananas” are not the same length
  • “bananab” and “abanana” are subsets but not anagrams
  • “Tom Riddle” and “I’m Lord Voldemort?”

program

The first step is to check our arguments for correctness and if we need bother testing. We’ll remove spaces and punctuation before testing if strings are the same, and we’ll force all the characters to lowercase since lettercase doesn’t matter. Naturally, you may wish to use console arguments, ask for user input, or a load a list to test anagrams.

Note: if asked instead to find if two strings are permutations of each other, it’s even easier: skip the character removal.


C#

Note the use of System.Linq.Enumerable.SequenceEqual to compare arrays since in C# Array.Equals compares instances, not contents. If testing this code in Visual Studio 2013, remember to use Start Without Debugging to see the console window. In Java you can use System.out.println instead of System.Console.WriteLine, and see String.replaceAll for regular expression replacement.

C#: are two strings anagrams
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestQuestions
{
class Program
{
static void Main(string[] args)
{
string phrase1 = "School master";
string phrase2 = "!the classroom?";
if (TwoStringsAreAnagrams(phrase1, phrase2))
{
System.Console.WriteLine("{0} is an anagram of {1}", phrase1, phrase2);
}
else
{
System.Console.WriteLine("{0} is NOT an anagram of {1}", phrase1, phrase2);
}
}
/// <summary>
/// One method to test if two strings are anagrams.
/// Assumes gibberish counts as a word or phrase.
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
static bool TwoStringsAreAnagrams(string str1, string str2)
{
if (str1 == null || str2 == null)
{
return false;
}
// remove spaces and puncuation
str1 = System.Text.RegularExpressions.Regex.Replace(str1, "[\\s\\p{P}]", "");
str2 = System.Text.RegularExpressions.Regex.Replace(str2, "[\\s\\p{P}]", "");
str1 = str1.ToLower();
str2 = str2.ToLower();
if (str1.Equals(str2))
{
return false;
}
if (str1.Length == str2.Length)
{
char[] char1Array = str1.ToCharArray();
char[] char2Array = str2.ToCharArray();
// in C# Array.Equals compares instance, not contents
// using System.Linq to compare sorted arrays
Array.Sort(char1Array);
Array.Sort(char2Array);
return Enumerable.SequenceEqual(char1Array, char2Array);
}
return false;
}
}
}

Java

Java: are two strings anagrams
public class StringProblems
{
static boolean areAnagrams(String phrase1, String phrase2)
{
if (phrase1 != null && phrase2 != null && phrase1.length() > 0 && phrase2.length() > 0)
{
// spaces, punctuation, case don't count
String pattern = "[\\s\\p{P}]";
phrase1 = phrase1.replaceAll(pattern, "").toLowerCase();
phrase2 = phrase2.replaceAll(pattern, "").toLowerCase();
if (phrase1.equals(phrase2)) {
// same phrase is not an anagram
return false;
}
// convert to array and sort
char[] phr1Array = phrase1.toCharArray();
char[] phr2Array = phrase2.toCharArray();
Arrays.sort(phr1Array);
Arrays.sort(phr2Array);
if (Arrays.equals(phr1Array, phr2Array))
{
return true;
}
}
return false;
}
}

Objective-C

Being true to our observation about sorted letters, in this example I convert the sorted arrays back to strings for comparison using a trick on the NSString.description and component joining I found on stackoverflow.com: Convert NSArray to NSSstring in Objective-C.

Objective-C: are two strings anagrams
@implementation TestQuestions
/**
* One method to check if two strings are anagrams.
* Assumes gibberish strings are valid.
*/
+ (BOOL)IsAnagram:(NSString *)anagram ofPhrase:(NSString *)phrase
{
if (anagram && phrase) {
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[\\s\\p{P}]"
options:NSRegularExpressionCaseInsensitive error:&error];
anagram = [regex stringByReplacingMatchesInString:anagram
options:0
range:NSMakeRange(0, anagram.length)
withTemplate:@""];
phrase = [regex stringByReplacingMatchesInString:phrase
options:0
range:NSMakeRange(0, phrase.length)
withTemplate:@""];
anagram = [anagram lowercaseString];
phrase = [phrase lowercaseString];
if ([anagram isEqualToString:phrase]) {
return NO;
}
if (anagram.length == phrase.length) {
// build arrays of single-character strings so we can sort and join
NSMutableArray *anagramArray = [NSMutableArray array];
NSMutableArray *phraseArray = [NSMutableArray array];
for (int i = 0; i < phrase.length; ++i) {
NSString *ch = [NSString stringWithFormat:@"%c", [phrase characterAtIndex:i]];
[phraseArray addObject:ch];
ch = [NSString stringWithFormat:@"%c", [anagram characterAtIndex:i]];
[anagramArray addObject:ch];
}
NSArray *sortedAnagramArray = [anagramArray sortedArrayUsingSelector:@selector(compare:)];
NSArray *sortedPhraseArray = [phraseArray sortedArrayUsingSelector:@selector(compare:)];
// this method of joining components is only useful for arrays of strings
NSString *sortedAnagramString = [[sortedAnagramArray valueForKey:@"description"] componentsJoinedByString:@""];
NSString *sortedPhraseString = [[sortedPhraseArray valueForKey:@"description"] componentsJoinedByString:@""];
if ([sortedAnagramString isEqualToString:sortedPhraseString]) {
return YES;
}
}
}
return NO;
}
@end

Swift 2.1

What if we wanted to allow “Ray.adverb” as a single string counting the period? Of course, “ray-adverb” should still count as two words and ignore the hypen. Instead of stripping all punctuation, we’d need to consider word boundaries. Let’s try this in a Swift Playground since Playgrounds make it easy to try things out and see the changes.

Swift 2.1: are two strings anagrams
import Cocoa
var str1 = "Astronomer!"
var str2 = " Moon starer?"
var str3 = "ray-adverb"
var str4 = "barry-dave."
var str5 = "Ray.Adverb"
var str6 = "Tom Marvolo-Riddle"
var str7 = "I am Lord Voldemort!"
func areAnagrams(phrase1: String, phrase2: String) -> Bool
{
if phrase1.isEmpty { return false }
if phrase2.isEmpty { return false }
//var trimmedPhrase1 = phrase1.stringByReplacingOccurrencesOfString(" ", withString: "")
//var trimmedPrhase2 = phrase2.stringByReplacingOccurrencesOfString(" ", withString: "")
var trimmedPhrase1 = stripWordBoundaries(phrase1)
var trimmedPrhase2 = stripWordBoundaries(phrase2)
trimmedPhrase1 = trimmedPhrase1.lowercaseString
trimmedPrhase2 = trimmedPrhase2.lowercaseString
if trimmedPrhase2 == trimmedPhrase1 {
return false
}
if trimmedPrhase2.characters.count != trimmedPhrase1.characters.count {
return false
}
let chars1 = trimmedPhrase1.characters.sort()
let chars2 = trimmedPrhase2.characters.sort()
if chars1 == chars2 {
return true
}
return false
}
// let's treat "Ray.adverb" as a word so period would not be stripped, but "Adverb-Ray?" would become AdverbRay
func stripWordBoundaries(string: String) -> String
{
var words : [String] = []
string.enumerateSubstringsInRange(string.characters.indices,
options: .ByWords) {
(substring, _, _, _) -> () in
words.append(substring!)
}
return words.joinWithSeparator("")
}
func anagramTest(phrase1 ph1: String, phrase2 ph2: String)
{
if areAnagrams(ph1, phrase2: ph2) {
print("'\(ph1)' and '\(ph2)' are anagrams")
}
else {
print("'\(ph1)' and '\(ph2)' are NOT anagrams")
}
}
anagramTest(phrase1: str1, phrase2: str2)
anagramTest(phrase1: str3, phrase2: str4)
anagramTest(phrase1: str5, phrase2: str4)
anagramTest(phrase1: str6, phrase2: str7)

Swift 3

Swift 3: are two strings anagrams
func AreAnagrams(phrase1: String, phrase2: String) -> Bool
{
if (phrase1.characters.count > 0 && phrase2.characters.count > 0) {
let regex = try! RegularExpression(pattern: "[\\s\\p{P}]", options: [.caseInsensitive])
let trimmedPhrase1 = regex.stringByReplacingMatches(in: phrase1, options: [], range: NSMakeRange(0, phrase1.characters.count), withTemplate: "")
let trimmedPhrase2 = regex.stringByReplacingMatches(in: phrase2, options: [], range: NSMakeRange(0, phrase2.characters.count), withTemplate: "")
let chArr1 = Array(trimmedPhrase1.lowercased().characters)
let chArr2 = Array(trimmedPhrase2.lowercased().characters)
if (chArr1 == chArr2) {
return false;
}
let sorted1 = chArr1.sorted()
let sorted2 = chArr2.sorted()
if sorted1 == sorted2 {
return true;
}
}
return false;
}
AreAnagrams(phrase1: "Dave Barry!", phrase2: "Davebarry")