The Solution
A major hint is in the fact we are given a dictionary. Because we are given this dictionary we can prep it in any way we like when the program starts, and then the processing of any word input becomes simple. Most dictionaries will easily fit within memory of modern computing hardware, so this should not be a major concern.
So, having the dictionary, we have all possible valid word anagrams already. The only trick is how to get from the input to these words.
So let’s think, “what do all anagrams have in common?” For example, the word “post” has “pots”, “stop”, “tops”, etc. It may seem obvious, but all anagrams have the same letters. Thus, if we can hash or organize these letters in a consistent way, we can store all anagrams under the same key. In this way, we can apply the same hash/organization to the input word, immediately get the list of anagrams, and simply exclude the input word from the anagram list.
The simplest way to do this would simply be to sort the letters, this is a fairly simple operation in C#:
var key = string.Concat(word.ToLower().OrderBy(c => c));
That would turn “post”, “pots”, etc. all into: “opst” which we can use as our key for our lookup. Now, how to store them, you could download your own multidictionary, or create a Dictionary<string, List<string>>,but really C# already has one for you called a Lookup. The Lookup stores a key to multiple values.
So first, let’s write a simple DAO for reading in our words from a file:
public class WordFileDao : IWordDao { public string FileName { get; private set; } public WordFileDao(string fileName) { FileName = fileName; } public IEnumerable<string> RetrieveWords() { // make sure to remove any accidental space and normalize to lower case return File.ReadLines(FileName).Select(l => l.Trim().ToLower()); } }
Then, we can create an anagram finder to create the lookup on construction, and then find words on demand:
public class AnagramFinder { private readonly ILookup<string, string> _anagramLookup; public AnagramFinder(IWordDao dao) { // construct the lookup at initialization using the // dictionary words with the sorted letters as the key _anagramLookup = dao.RetrieveWords().ToLookup( k => string.Concat(k.OrderBy(c => c))); } public IList<string> FindAnagrams(string word) { // at lookup time, sort the input word as the key, // and return all words (minus the input word) in the sequence string input = word.ToLower(); string key = string.Concat(input.OrderBy(c => c)); return _anagramLookup[key].Where(w => w != input).ToList(); } }
Notice the ToLookup() extension method (in System.Linq), this method creates an instance of Lookupfrom any IEnumerable<T> if you provide it a key generator and a value generator. For the key generator, I’m returning the word in sorted, lowercase (for consistency). For the value, I’m just lower-casing the word (again, for consistency). This effectively creates the “dictionary of key to list of values” that, when you query using the “[…]” operator, will return an IEnumerable<T> of the values, or empty sequence if the key was not found.
And that’s it! We have our anagram word finder which can lookup words quickly with only the cost of sorting the letters in a word, which is much less expensive (processing-wise) than attempting all permutations of the letters in a word.
Quote From: