Number Words
July 25, 2014
Today’s exercise is an interview question from Career Cup. When I first saw the exercise, I thought it would be easy, but I had trouble writing a solution, so maybe it will a good exercise for all you readers, as well:
Given a positive integer, return all the ways that the integer can be represented by letters using the mapping 1 -> A, 2 -> B, …, 26 -> Z. For instance, the number 1234 can be represented by the words ABCD, AWD and LCD.
Your task is to write the program to generate words from numbers. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
In Python. words form a number and a number from a word.
In perl this returns a list of the words!
How would a number like 30 be represented? Should that just be a null list?
My function ignores the zero and returns “C” but there is no proper answer since it is always possible to add a string of zeros in the middle of the input. Feel free to make your own rule about the proper treatement of zero.
Recursive version in Python. I started the mapping at 0 -> ‘A’, so that numbers with zeros in them could be converted.
My solution above has a bug that causes duplicates to be generated when the number ends in a ‘2’.
Here’s a revised solution:
Nice problem. I ended up with a sort of dynamic programming solution, C++11:
The following is a C solution. Without the willingness to write a FIFO data set, I had to rely on recursion to get the problem completed. It took about 20 minutes and runs pretty fast. With the queue library, I could implement an iterative approach that would likely use less memory for larger numbers. Without further ado:
C:\Users\Dragonborn\Documents\CodeBlocks\numberwords\main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 typedef unsigned long long uint;
6
7 void printNumberWords(uint number);
8 void printNumberWords_aux(char* number_str, char* path);
9 char getLetterFromPrefix(char* str, unsigned int n);
10 char* appendCString(const char* str, const char c);
11
12 unsigned int countDigits(uint number);
13 char* uint2string(uint number);
14
15 int main()
16 {
17 printNumberWords(121223213123);
18 return 0;
19 }
20
21 void printNumberWords(uint number)
22 {
23 char* number_str = uint2string(number);
24 const unsigned int LEN = strlen(number_str);
25
26 char option1 = (LEN >= 1) ? getLetterFromPrefix(number_str, 1) : ('');
27 char option2 = (LEN >= 2) ? getLetterFromPrefix(number_str, 2) : ('');
28
29 if(option1 != '')
30 {
31 if(LEN == 1)
32 printf("%c\n", option1);
33 else
34 {
35 char* path = appendCString("", option1);
36 printNumberWords_aux(number_str + 1, path);
37 free(path);
38 }
39 }
40 if(option2 != '')
41 {
42 if(LEN == 2)
43 printf("%c\n", option2);
44 else
45 {
46 char* path = appendCString("", option2);
47 printNumberWords_aux(number_str + 2, path);
48 free(path);
49 }
50 }
51 free(number_str);
52 }
53
54 void printNumberWords_aux(char* number_str, char* path)
55 {
56 const unsigned int LEN = strlen(number_str);
57
58 char option1 = (LEN >= 1) ? getLetterFromPrefix(number_str, 1) : ('');
59 char option2 = (LEN >= 2) ? getLetterFromPrefix(number_str, 2) : ('');
60
61 if(option1 != '')
62 {
63 if(LEN == 1)
64 printf("%s%c\n", path, option1);
65 else
66 {
67 char* new_path = appendCString(path, option1);
68 printNumberWords_aux(number_str + 1, new_path);
69 free(new_path);
70 }
71 }
72 if(option2 != '')
73 {
74 if(LEN == 2)
75 printf("%s%c\n", path, option2);
76 else
77 {
78 char* new_path = appendCString(path, option2);
79 printNumberWords_aux(number_str + 2, new_path);
80 free(new_path);
81 }
82 }
83 }
84
85 char getLetterFromPrefix(char* str, unsigned int n)
86 {
87 static const char* LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
88 static const unsigned int LETTERS_LEN = 26;
89
90 char* nstr = malloc(sizeof(char) * (n+1));
91 nstr[n] = '';
92 memcpy(nstr, str, n * sizeof(char));
93
94 int number = atoi(nstr);
95 free(nstr);
96
97 if(number <= LETTERS_LEN && number > 0)
98 return LETTERS[number - 1];
99 return '';
100 }
101
102 char* appendCString(const char* str, const char c)
103 {
104 const unsigned int LEN = strlen(str);
105 char* rValue = malloc(sizeof(char) * (LEN + 2));
106 memcpy(rValue, str, LEN);
107 rValue[LEN + 0] = c;
108 rValue[LEN + 1] = '';
109 return rValue;
110 }
111
112
113 char* uint2string(uint number)
114 {
115 const unsigned int NUMBER_LEN = countDigits(number);
116 unsigned int pos = NUMBER_LEN - 1;
117 char* rValue = malloc(sizeof(char) * NUMBER_LEN + 1);
118 rValue[NUMBER_LEN] = '';
119 while(number > 0)
120 {
121 rValue[pos] = (number % 10) + '0';
122 number /= 10;
123 pos --;
124 }
125 return rValue;
126 }
127
128 unsigned int countDigits(uint number)
129 {
130 unsigned int rValue = 0;
131 while(number > 0)
132 {
133 rValue++;
134 number /= 10;
135 }
136 return rValue;
137 }
That is a tricky interview question considering the ambiguity with zero.
It is interesting to see how the different solutions react to
hostile inputs containing zeros. There might be some surprises.
My Haskell solution maps 0 to @.
Output:
Good point about zeroes – I’m inclined to just say there aren’t any matches for eg. “30”.
Here’s a nice and simple recursive solution in C (my other solution avoids some duplicated effort in the recursive calls, but the space requirements get quite large). This generates the solutions in lexicographic order too.
There are 259584 solutions for the string “1234567891011121314151617181920212223242526” incidentally (including “ABCDEFGHIJKLMNOPQRSTUVWXYZ” of course).
Whee Rackety goodness!
Full writeup: Number words
I’ve been meaning to do this for a while – here’s an improved version of my “dynamic programming” solution; this one uses a tree structure to store the labellings rather than an explicit list (which gets rather large). Now, we can calculate that eg. ‘12345678910111213141516171819202122232425261234567891011121314151617181920212223242526’ has 67383853056 labellings, which would take a long time to generate explicitly. It also fixes a bug which mishandled the case where no labelling was possible.