Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 567
poorvank Avatar asked Mar 23 '13 11:03

poorvank


People also ask

How do I decode a string?

So first, write the very first character of the encoded string and remove it from the encoded string then start adding the first character of the encoded string first to the left and then to the right of the decoded string and do this task repeatedly till the encoded string becomes empty.

How many ways can an encoded message be decoded?

Given the mapping a = 1, b = 2, ... z = 26, and an encoded message, count the number of ways it can be decoded. For example, the message '111' would give 3, since it could be decoded as 'aaa', 'ka', and 'ak'. You can assume that the messages are decodable. For example, '001' is not allowed.

How to count the number of ways a string can be decoded?

Suppose we have mapping like 'a' = 1, 'b' = 2, ... 'z' = 26, and we have an encoded message message string, we have to count the number of ways it can be decoded. So, if the input is like message = "222", then the output will be 3, as This can be decoded 3 ways: bbb, bv, and vb.

How do I encode a message into a number?

A message containing letters from A-Z can be encoded into numbers using the following mapping: 'A' -> "1" 'B' -> "2" ... 'Z' -> "26" To decode an encoded message, all the digits must be grouped then mapped back into letters using the reverse of the mapping above (there may be multiple ways). For example, "11106" can be mapped into:

How do we initialize the total count of decodings?

We initialize the total count of decodings as 0. We recur for two subproblems. 1) If the last digit is non-zero, recur for the remaining (n-1) digits and add the result to the total count. 2) If the last two digits form a valid character (or smaller than 27), recur for remaining (n-2) digits and add the result to the total count.


2 Answers

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.

like image 98
Alexander Avatar answered Oct 24 '22 02:10

Alexander


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
like image 39
James Baye Avatar answered Oct 24 '22 02:10

James Baye