How do we determine if two strings are anagrams?

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 (updated for Swift 5).

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 5

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 5.2: are two strings anagrams
import UIKit
var stringList: [String] = ["Tom Marvolo Riddle", "School master"]
stringList.append("I am Lord Voldemort!")
stringList.append("the classroom")
stringList.append("Ray-Adverb")
stringList.append("Ray adverb")
stringList.append("Astronomer!!")
stringList.append("moon starer?")
stringList.append("Dave Barry")
for str1 in stringList {
for str2 in stringList {
if (areAnagrams(phrase1: str1, phrase2: str2)) {
// print to debugger window
print("'" + str1 + "' and '" + str2 + "' are anagrams")
}
}
}
func areAnagrams(phrase1: String, phrase2: String) -> Bool {
if (phrase1.count > 0 && phrase2.count > 0) {
// define regular expression to remove spaces and punctuation
let regex = try! NSRegularExpression(pattern: "[\\s\\p{P}]", options: [.caseInsensitive])
let trimmedPhrase1 = regex.stringByReplacingMatches(in: phrase1, options: [], range: NSMakeRange(0, phrase1.count), withTemplate: "")
let trimmedPhrase2 = regex.stringByReplacingMatches(in: phrase2, options: [], range: NSMakeRange(0, phrase2.count), withTemplate: "")
// make a lower-case character array to compare
let chArr1 = Array(trimmedPhrase1.lowercased())
let chArr2 = Array(trimmedPhrase2.lowercased())
if (chArr1 == chArr2) {
// same string, not an anagram
return false;
}
// sort to compare
let sorted1 = chArr1.sorted()
let sorted2 = chArr2.sorted()
if sorted1 == sorted2 {
return true;
}
}
return false
}