16
votes

You are given an encoded message containing only numbers. You are also provided with the following mapping

 A : 1
 B : 2
 C : 3
 ..
 Z : 26

Given an encoded message, count the number of ways it can be decoded.

eg: 12 can be decoded in 2 ways: (A,B) and (L)

I came up with the algorithm of accepting the numbers as a character of strings and then checking for each of its digit:

1.If the first digit of the string array is zero , return zero.

2.for each of its digit(i) from 1 to n perform:

   if str[i-1]>2 || (str[i-1]=='2' && str[i]>'6')
      return 0;

   if(str[i]==0)
      return 0;

Each time i try to encode the first digit in the message to a letter, or i can encode the first two digits into a letter if possible. When there is no way to encode like encountering a single ’0′, or encountering ’32′, simply return.

Can this problem be solved more efficiently?

7
The encoding method is not clear. How is '1' defined separately from '11' in the encoding method for example ? - suspectus
According to the mapping provided A->1..and K->11..So the number of ways in which 11 can be decoded is 2 (A,A corresponding to 1,1) and (k corresponding to the number 11) - poorvank
@poorvank, for what I can read you aren't counting anything yet though - Alexander

7 Answers

29
votes

Your current approximation to the problem is correct. Although, you need to be really careful that you are handling all the cases which it is not clear and this will make my answer a bit longer than needed.

A correct way to see this problem is from a Dynamic Programming perspective. Let's consider your input string as message and its length as n.

To decode a message of n characters, you need to know in how many ways you can decode message using n - 1 characters and a message using n - 2 characters. That is,

A message of n characters.

                                              1
          1   2   3   4   5   6   7   8   9   0   1
        +---+---+---+---+---+---+---+---+---+---+---+
message | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 1 | 4 | 1 | 2 |
        +---+---+---+---+---+---+---+---+---+---+---+

Using a 1 digit and a message of n - 1 characters long.

                                              1
          1   2   3   4   5   6   7   8   9   0       1
        +---+---+---+---+---+---+---+---+---+---+   +---+
message | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 1 | 4 | 1 | + | 2 |
        +---+---+---+---+---+---+---+---+---+---+   +---+

Using a 2 digits and a message of n - 2 characters long.

                                                  1
          1   2   3   4   5   6   7   8   9       0   1
        +---+---+---+---+---+---+---+---+---+   +---+---+
message | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 1 | 4 | + | 1 | 2 |
        +---+---+---+---+---+---+---+---+---+   +---+---+

Now, you may ask yourself:

How do I calculate in how many ways you can decode message of n - 1 characters and of n - 2 characters?

It's actually in the same way. Eventually you will reduce it to its base case.

Let's say ways[n] its the number of ways you can decode message of n characters. Then, you can put ways[n] in this way,

ways[n] = ways[n - 1] + ways[n - 2]

(Since there is no clue how you'd define the number of ways for an empty string I considered it as 1.)

With proper constraints and base case,

  • n = 0,

     ways[n] =  1
    
  • n > 1 and message[n] is valid and message[n - 1:n] is valid,

     ways[n] = ways[n - 1] + ways[n - 2]
    
  • n > 1 and message[n] is valid and message[n - 1:n] is not valid,

     ways[n] = ways[n - 1]
    
  • n > 1 and message[n] is not valid and message[n - 1:n] is valid,

     ways[n] = ways[n - 2]
    
  • otherwise,

     ways[n] = 0
    

An iterative decode function in C may look as follows,

int decode(char* message, size_t len) {
    int i, w, ways[] = { 1, 0 };
    for(i = 0, w; i < len; ++i) {
        w = 0;
        if((i > 0) && ((message[i - 1] == '1') || (message[i - 1] == '2' && message[i] < '7'))) {
            w += ways[1];
        }
        if(message[i] > '0') {
            w += ways[0];
        }
        ways[1] = ways[0];
        ways[0] = w;
    }
    return ways[0];
}

You can see it here at ideone. I'm using constant extra memory for the calculation.

1
votes

Thought I'd complement @Alexander's post with my commented python code, as it took me a bit of time to get my head around exactly how the dynamic programming solution worked. I find it useful to realise how similar this is to coding the Fibonacci function. I've also uploaded full code, tests and runtime comparison with naive recursion on my github.

def number_of_decodings_fast(code):
    """ Dynamic programming implementation which runs in 
    O(n) time and O(1) space. 
    The implementation is very similar to the dynamic programming
    solution for the Fibonacci series. """
    length = len(code)
    if (length <= 1):
        # assume all such codes are unambiguously decodable
        return 1
    else:
        n_prev = 1 # len 0
        n_current = 1 # len 1
        for i in range(1,length):
            if (
                # a '1' is ambiguous if followed by
                # a digit X=[1-9] since it could be
                # decoded as '1X' or '1'+'X'
                code[i-1]=='1' and 
                code[1] in [str(k) for k in (range(1,10))]
            ) or (
                # a '2' is ambiguous if followed by 
                # a digit X=[1-6] since it could be
                # decoded as '2X' or '2'+'X'
                code[i-1]=='2' and 
                code[i] in [str(k) for k in range(1,7)]
            ):
                # New number of decodings is the sum of
                # decodings obtainable from the code two digits back
                # (code[0:(i-2)] + '[1-2]X' interpretation)
                # and decodings obtainable from the code one digit back
                # (code[0:(i-1)] + 'X' interpretation).
                n_new = n_prev + n_current
            else:
                # New number of decodings is the same as
                # that obtainable from the code one digit back
                # (code[0:(i-1)] + 'X' interpretation).
                n_new = n_current
            # update n_prev and n_current
            n_prev = n_current
            n_current = n_new
        return n_current
0
votes

Recursive solution

int countdecodes(char * s)
{
    int r = 0;
    switch(*s)
    {
        case 0:
            return 1;
        case '0':
            return 0;
        case '1':
            r = countdecodes(s+1);
            if(*(s+1))
                r = r + countdecodes(s+2);
            return r;
        case '2':
            r = countdecodes(s+1);
            switch(*(s+1))
            {
                case '0': case '1': case '2':
                case '3': case '4': case '5':
                case '6':
                    r = r + countdecodes(s+2);
                default:
                    return r;
            }
        case '3': case '4': case '5': case '6':
        case '7':case '8': case '9':
            return countdecodes(s+1);
        default:
            return 0;
    }
}

Sample returns

  1. countdecodes("123"); // returns 3
  2. countdecodes("1230"); // returns 0
  3. countdecodes("1220"); // returns 2
  4. countdecodes("12321"); // returns 6
0
votes

This is a small and simple O(n) solution for the problem, using dynamic programming:

int ways(string s){
    int n = s.size();
    vector<int> v(n + 1, 1);
    v[n - 1] = s[n - 1] != '0';
    for(int i = n - 2; i >= 0; i--){
        if(s[i] == '0') {v[i] = v[i + 1]; continue;}
        if(s[i] <= '2' && s[i + 1] <= '6') 
            if(s[i + 1] != '0') v[i] = v[i + 1] + v[i + 2];
            else v[i] = v[i + 2];
        else v[i] = v[i + 1];
    }
    return v[0];
}

The idea is to walk index by index. If that index (attached to the next) contains a number less or equal to 26 and no zeros, it is divided in 2 possibilities, otherwise just one.

ways("123");    //output: 3
ways("1230");   //output: 0
ways("1220");   //output: 2
ways("12321");  //output: 6
0
votes
def nombre_codage(message):
    map=[]
    for i in range(1,27,1):
        map.append(i)
    nbr=1
    n=len(message)
    pair_couple=0
    for j in range (0,n,2):
        if int(message[j:j+2]) in map and len(message[j:j+2])==2:
            pair_couple+=1
    nbr+=2**pair_couple-1 
    impair_couple=0
    for k in range (1,n,2):
        if int(message[k:k+2]) in map and len(message[k:k+2])==2:
            impair_couple+=1
    nbr+=2**impair_couple-1    
    return nbr 

Here's a solution in Python, it takes the message as a string then counts the digits of two numbers that could be encoded and using the binomial coefficient -- I used another form of it (C(n-p)=2^n)-- it counts how many ways you can encode the message.

0
votes

I am having a simple pattern based solution for this problem having O(N) time complexity and O(1) space complexity.

Explanation Example:-

12312

                              1 
                     /                 \
                  1,2                   12
                /     \                   \
            1,2,3      1,23                12,3 
            |             |                    \
        1,2,3,1           1,23,1                12,3,1
        /    \           /       \             /       \
1,2,3,1,2   1,2,3,12   1,23,1,2   1,23,12   12,3,1,2   12,3,12


P1 P2 N  T
1  0  1  1
1  1  2  2
2  1  2  3
3  0  1  3
3  3  2  6

P1 represents number of cases with new element not forming a 2 digit number

P2 represents number of cases with new element forming a 2 digit number

N=1 represents case related to p1 and N=2 represents case related to p2

So a new tree with respect to case 1 and 2 would be like

           1
       /       \
      1         2
   /     \       \
  1       2       1
  |       |       |
  1       1       1
 / \     / \     / \
1   2   1   2   1   2
#include <iostream>
#include <string>
using namespace std;


int decode(string s)
{
    int l=s.length();
    int p1=1;
    int p2=0;
    int n;
    int temp;
    for(int i=0;i<l-1;i++)
    {
        if(((int(s[i]-'0')*10)+int(s[i+1]-'0'))<=26)
        {
            n=2;
        }
        else
        {
            n=1;
        }
        temp=p2;
        p2=(n==2)?p1:0;
        p1=p1+t;
    }
    return p1+p2;
}

int main() {
    string s;
    getline(cin,s);
    cout<<decode(s);
    return 0;
}

It is only valid for characters from 1 to 9.

0
votes

Recursive solution

list = []
def findCombiation(s,ls):
    if len(s) == 0:
        list.append(ls)
    ls = ls + "0"
    if s[0:1] == "0":
        findCombiation(s[1:],ls)
    else:
        st = s[:2]
        if st == "":
            return 
        if int(st) > 25 :
            findCombiation(s[1:],ls)
            ls = ls + s[:1]
        else:
            findCombiation(s[1:],ls+s[:1])
            findCombiation(s[2:],ls+s[:2])

findCombiation("99","") 
print(set(list))