Possible combinations of characters on a phone's numpad

676 views Asked by At

I was faced with a question recently in C. We have a phone's numpad with following layout:

1[abc] 2[def] 3[ghi]
4[jkl] 5[mno] 6[pqr]
7[st]  8[uv]  9[wx]
       0[yz]

How to come up with an API which gives all possible combinations of characters belonging to each number for a given numeral input. For e.g. input = 1234

Then the API should print all possible combinations of characters-

adgj bdgj cdgj aegj begj cegj.. and so on.

Is there a simple way to do it? Apart from hardcoded nested for loops. I was given a hint as recursion but couldn't figure a way out of it.

4

There are 4 answers

0
user3386109 On

Recursion is just a sneaky way of nesting four for loops. Here's what the code looks like

#include <stdio.h>

void sneaky( int depth, int maxDepth, char str[] )
{
    char c, start;

    start = 'a' + depth * 3;
    for ( c = start; c < start + 3; c++ )
    {
        str[depth] = c;
        str[depth+1] = '\0';

        if ( depth == maxDepth )
            printf( "%s\n", str );
        else
            sneaky( depth + 1, maxDepth, str );
    }
}

int main( void )
{
    char str[5] = { 0 };
    sneaky( 0, 3, str );
}

You can also solve this problem, and similar combinatorial problems, with a simple counting algorithm. A counting algorithm emulates natural counting, in which you increment the least significant digit from 0 to 9. When the least significant digit wraps from 9 back to 0, the next digit to the left is incremented.

The same can be done to solve the OP's problem. But in this case, the digits have either two or three possible values. And if you examine the pattern in the OP, it's readily apparent that the least significant digit is on the left. In the pattern
adgj bdgj cdgj aegj
you can see that a becomes b, b becomes c, and when c wraps back to a, then d becomes e.

Here's the code

#include <stdio.h>
#include <stdlib.h>

static char InitialValue[] = { 'y', 'a', 'd', 'g', 'j', 'm', 'p', 's', 'u', 'w' };

static char NextValue[] = { 'b', 'c', 'a',   'e', 'f', 'd',   'h', 'i', 'g',
                            'k', 'l', 'j',   'n', 'o', 'm',   'q', 'r', 'p',
                            't', 's',   'v', 'u',   'x', 'w',   'z', 'y'     };

static void error( char *msg )
{
    fprintf( stderr, "%s\n", msg );
    exit( EXIT_FAILURE );
}

int main( void )
{
    int i, oldDigit;
    char str[12];

    // get the input string from the user
    printf( "Enter the input string: " );
    fflush( stdout );
    if ( scanf( "%10s", str ) != 1 )
        error( "whatever" );

    // convert the input string to the corresponding first output string
    for ( i = 0; str[i] != '\0'; i++ )
    {
        if ( str[i] < '0' || str[i] > '9' )
            error( "invalid input string" );
        str[i] = InitialValue[str[i] - '0'];
    }
    printf( "%s\n", str );

    // use a simple counting algorithm to generate the string combinations
    for (;;)
    {
        for ( i = 0; str[i] != '\0'; i++ )
        {
            oldDigit = str[i];                   // save the current digit
            str[i] = NextValue[oldDigit - 'a'];  // advance the digit to the next value 
            if ( str[i] > oldDigit )             // if the digit did not wrap
                break;                           // then we've got a new string
        }

        if ( str[i] == '\0' )                    // if all the digits wrapped
            break;                               // then we're done
        printf( "%s\n", str );                   // output the new string
    }

    return( EXIT_SUCCESS );
}
0
UncleO On

(That is not the standard layout for a phone, by the way.)

The tricky part is handling the data structures. It is handy that the input string consists of numbers, because then we can use the digits in the string to index an array that holds the possible letters for each number.

The idea is to modify an output string at a particular index using a for loop to go over all the possible replacements at that index. Then recursively move to the next index in the output array in the body of the for loop.

If you reach the end of the array, then print and return.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char* data[] = {"0", "1", "2abc", "3def", "4ghi", "5jkl", "6mno", "7prs", "8tuv", "9wxy"};

char* input = "23456783";

char* arr;

void current(int index)
{
    if(index == strlen(input)) printf("%s\n", arr);
    else
    {  
         for(int i = 0; i < strlen(data[input[index] - '0']); ++i)
         {
              arr[index] = data[input[index] - '0'][i];
              current(index + 1);
         }
    }
}

void main()
{
    arr = malloc(strlen(input) + 1);
    arr[strlen(input)] = '\0';
    printf("%s\n\n", input);
    current(0);
}
1
M Oehm On

Recursion is a good solution for such problems, where you must find combinations. The advantage over nested loops is that recursion works for strings of any length.

In your case, you need a function that takes:

  • the original string
  • an auxiliary char buffer for the solution* and
  • the current index, which starts at 0.

Recursive functions require a termination condition: When you have reached the end of the original string, print it and return.

Otherwise, take the next digit, check whether it is valid, determine the letters associated with it and then call the function for each of the letters. That is, for each letter, copy it to the solution at the current index, then call the function with the next index.

Below's an example implementation that uses an intermediate function to do some house-keeping:

#include <stdlib.h>
#include <stdio.h>



/*
 *      Recursive back-end, that produces all combinations in sol.
 */
void alpha_r(const char *str, char *sol, int index)
{
    const char *combo[] = {
        "yz", "abc", "def", "ghi", "jkl", "mno", "pqr", "st", "uv", "wx" 
    };

    if (str[index] == '\0') {
        printf("%s\n", sol);
    } else {
        int k = str[index] - '0';
        const char *p = combo[k];

        while (*p) {
            sol[index] = *p++;
            alpha_r(str, sol, index + 1);
        }        
    }
}

/* 
 *      Non-recursive front-end that checks the string for validity
 *      and creates a temporary buffer for the solutions.
 */    
void alpha(const char *str)
{
    int len = 0;

    while (str[len]) {
        if (str[len] < 0 || str[len] > '9') {
            fprintf(stderr, "Invalid input.\n");
            return;
        }
        len++;
    }

    char sol[len + 1];

    sol[len] = '\0';
    alpha_r(str, sol, 0);
}

int main()
{
    alpha("123");

    return 0;
}

*) You could also use the string itself to store the solutions.

4
T.Woody On

A way to find the combinations that you are looking for could be bitwise logic, with a binary number and an integer. The binary number would be as long as the string, with 0's and 1's acting as on and off switches for what is included and excluded in the string. The thing here is that we use base 3 or 4 depending on the number "pressed", and

If base four, then some if statements have to be applied to move the ones along that are actually base three.