Ken Robertson's Blog

Ramblings of a .NET developer

Phone Number Conversion Challenge

Even though the1 already posted the his solution to his programming challenge, I was already amidst my own solution to it when he posted his.

I didn't think I was going to take a stab at it, since I figured plenty of other people would.  But as I saw people posting their solutions, it seemed like one thing was consistent in that most of them were taking the number, looking at how many ways the number could be represented as a string and then check if it was a word.

Then I thought, “what if you did it the other way around?”  What if you take a word, get its numerical form, and see if it was in the phone number.  Main reason being, a sequence of numbers has many possible string forms, but a word only has one possible numerical form.  I should note though that this is rather inefficient.  I used the basic English word list the1 posted, which was about 850 words.  It was rather limited since in his challenge post, he had an example number with a possible output of “nice-window”, but the word list didn't even have “nice“.  As your dictionary grows, it gets slower and slower.  Example being I took some of the dictionary files from ispell (unix spell checker) so it had a little over 100,000 entries.  Yeah, slow.  Eventually, the number of possible strings the number can be is less than the amount of time you'll spend looping through the dictionary.  I did it just to see how it'd work though.  Here is my “solution” in C#:

using System;
using System.Collections;
using System.IO;

namespace PhoneWord
{
   /// <summary>
   /// Summary description for Class1.
   /// </summary>
   class Finder
   {
      static ArrayList dictionary = new ArrayList();

      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      [STAThread]
      static void Main(string[] args)
      {
         string phoneNumber = "6423946369";   // The phone number
         // Load the dictionary
         LoadDictionary("dictionary.txt");
         FindWord(phoneNumber);            // Start finding them words!
      }

      /// <summary>
      /// This function loops through the dictionary, converts the words to their
      /// numerical form, and then looks for a match in the phone number. If there is a
      /// match, it replaces the numbers with the word, and then calls itself to find
      /// another word match.
      /// </summary>
      /// <param name="phoneNumber">The current phone number and any word matches already found.</param>
      static void FindWord(string phoneNumber)
      {
         foreach(string word in dictionary)
         {
            string tempNumber = phoneNumber;
            string metanumber = ConvertToMetanumber(word);
            if(tempNumber.IndexOf(metanumber) >= 0)
            {
               tempNumber = tempNumber.Replace(metanumber, word);
               Console.WriteLine("answer: " + tempNumber);
               FindWord(tempNumber);
            }
         }
      }

      /// <summary>
      /// This function loads all the entries from a dictionary file into an array list.
      /// The file is formatted with 1 word per line.
      /// </summary>
      /// <param name="filename">The filename of the dictionary file.</param>
      static void LoadDictionary(string filename)
      {
         StreamReader file = new StreamReader(filename);
         while(file.Peek() >= 0)
         {
            string word = file.ReadLine();
            if((word.Length < 10) || (word.IndexOf("'") == -1) || (word.Length != 0))
               dictionary.Add(word);
         }
         file.Close();
      }

      /// <summary>
      /// This function converts a word into its numerical representation.
      /// </summary>
      /// <param name="word">The word to be converted.</param>
      /// <returns>string</returns>
      static string ConvertToMetanumber(string word)
      {
         if(word == "")
            return "";
         else
         {
            // get a number referencing the char (A=0, B=1, C=2, ... Z=25)
            int asciiChar = Convert.ToInt32(Convert.ToChar(word.Substring(0, 1).ToUpper())) - 65;
            if(asciiChar > 17) asciiChar--;   // Trim it down by one after R, since 7 is PRQS
            if(asciiChar > 23) asciiChar--; // Trim it down by one more if Z, since 9 has WXYZ
            int asciiNumber = (asciiChar / 3) + 50;   // Divide by 3 to get which number it is on, add 50 for ASCII index
            return Convert.ToString(Convert.ToChar(asciiNumber)) + ConvertToMetanumber(word.Substring(1, word.Length-1));
         }
      }
   }
}

Posted: Apr 02 2004, 01:46 PM by qgyen | with 3 comment(s)
Filed under:

Comments

the1 said:

Hi Ken,

If you read the description of my algorithm in my post, you'll see that my approach is unlike either yours or theirs.

I do not enumerate all strings that represent the number, nor do I enumerate the dictionary to find out whether a word matches the prefix of the current number. Instead, I built a map from a digit sequence to all the words that match this sequence. It's a one time deal. After that, you just look up a digit sequence in the map (O(1) time if you implement the map as Hash or trie). It's actually very fast.
# April 2, 2004 4:58 PM

Ken Robertson said:

I'd started on mine before you'd posted your solution. I took a look at it and it does everything nicely. I was just sitting around and thought of converting the words instead of converting the number, and threw this together while trying to look productive at work. I think I was thinking about how to convert the ASCII codes to the numbers and figured I'd rather program it than stand there trying to figure it out. :)
# April 2, 2004 5:04 PM

orange county teachers credit union employment said:

orange county teachers credit union employment

# September 11, 2007 5:35 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)