Count possible decoding of a given digit sequence with hidden characters - GeeksforGeeks

27 Sep, 2021
Given a string S containing digits and character ‘*’ i.e. hidden character, the task is to find the number of ways to decode this hidden character of the given string.
Since the answer may be very large, return it modulo 109+7.
A string containing letters from A-Z can be encoded into numbers using the following mapping:
‘A’ -> “1” ‘B’ -> “2” ‘C’ -> “3” ‘D’ -> “4” ... ... ‘Z’ -> “26” Note: Characters including 0 are not included in the problem like (J → 10).
Examples: 
Input: s = “*”Output: 9Explanation: The encoded message can represent any of the encoded messages “1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, or “9”.Each of these can be decoded to the strings “A”, “B”, “C”, “D”, “E”, “F”, “G”, “H”, and “I” respectively.Hence, there are a total of 9 ways to decode “*”.
Input: s = “1*”Output: 18Explanation: The encoded message can represent any of the encoded messages “11”, “12”, “13”, “14”, “15”, “16”, “17”, “18”, or “19”.Each of these encoded messages have 2 ways to be decoded (e.g. “11” can be decoded to “AA” or “K”).Hence, there are a total of 9 × 2 = 18 ways to decode “1*”. 
Approach: 
This problem can be solved by observing that any constant number can be either decoded in a character which is a single-digit number or it can be decoded into a double-digit number if (i-1)th character is ‘1’ or (i-1)th character is ‘2’ and ith character is between 1 and 6. Therefore, the current state depends on the previous state, and dynamic programming can be used to solve the problem. Follow the steps below to solve the problem: 
1. Let dp[i] represent the number of ways to decode the string characters from 0 to i.
2. If the ith character is ‘*’ :
dp[i] = dp[i-1]*9 considering ‘*’ can be 1 to 9, and it is considered alone as a character.
Now, if the i and i-1 characters are combined, then,If (i-1)th character is ‘*’ then the two “**” together can form 15 possible characters(like 13 will form character ‘M’), so we add 15×dp[i-2] to dp[i].If (i-1)th character is ‘1’ then dp[i] = dp[i] + 9×dp[i-2] because the possible characters that can be decoded will be 11 to 19(K to S).If (i-1)th character is ‘2’ then dp[i] = dp[i] + 6×dp[i-2] as it can take value from 21 to 26.
If (i-1)th character is ‘*’ then the two “**” together can form 15 possible characters(like 13 will form character ‘M’), so we add 15×dp[i-2] to dp[i].
If (i-1)th character is ‘1’ then dp[i] = dp[i] + 9×dp[i-2] because the possible characters that can be decoded will be 11 to 19(K to S).
If (i-1)th character is ‘2’ then dp[i] = dp[i] + 6×dp[i-2] as it can take value from 21 to 26.
3. If the ith character is not ‘*’:
dp[i] = dp[i] + dp[i-1] considering ith character alone as a number.
Now, if it is possible to combine (i-1)th character and ith character together then add dp[i-2] to dp[i].
Below is the implementation of the above approach: 
C++
Java
Python3
C#
Javascript
#include <bits/stdc++.h>using namespace std; int M = 1000000007;int waysOfDecoding(string s){     vector<int> dp((int)s.size()+1);    dp[0] = 1;     // check the first character of the string    // if it is '*' then 9 ways     dp[1] = s[0] == '*'                ? 9                : s[0] == '0' ? 0 : 1;     // traverse the string    for (int i = 1; i < (int)s.size(); i++) {         // If s[i] == '*' there can be        // 9 possible values of *        if (s[i] == '*') {            dp[i + 1] = 9 * dp[i];             // If previous character is 1            // then words that can be formed            // are K(11), L(12), M(13), N(14)            // O(15), P(16), Q(17), R(18), S(19)            if (s[i - 1] == '1')                dp[i + 1]                    = (dp[i + 1] + 9 * dp[i - 1]) % M;             // If previous character is 2            // then the words that can be formed            // are U(21), V(22), W(23), X(24)Y(25), Z(26)            else if (s[i - 1] == '2')                dp[i + 1]                    = (dp[i + 1] + 6 * dp[i - 1]) % M;             // If the previous digit is * then            // all 15 2- digit characters can be            // formed            else if (s[i - 1] == '*')                dp[i + 1]                    = (dp[i + 1] + 15 * dp[i - 1]) % M;        }        else {            // taking the value from previous step            dp[i + 1] = s[i] != '0' ? dp[i] : 0;             // If previous character is 1 then            // the i-1th character and ith            // character can be decoded in            // a single character therefore,            // adding dp[i-1].            if (s[i - 1] == '1')                dp[i + 1]                    = (dp[i + 1] + dp[i - 1])                      % M;             // If previous character is 2            // and ith character is less than            // 6            // then the i-1th character and            // ith character can be decoded in            // a single character therefore,            // adding dp[i-1].            else if (s[i - 1] == '2'                     && s[i] <= '6')                dp[i + 1]                    = (dp[i + 1] + dp[i - 1]) % M;             // If previous character is * then            // it will contain the above 2 cases            else if (s[i - 1] == '*')                dp[i + 1]                    = (dp[i + 1]                       + (s[i] <= '6' ? 2 : 1)                             * dp[i - 1])                      % M;        }    }    return dp[(int)s.size()];} int main(){  string s = "12";  cout<<waysOfDecoding(s);   return 0;} // This code is contributed by mohit kumar 29.
// Java program for the above approach import java.io.*; class GFG {    static int M = 1000000007;    static int waysOfDecoding(String s)    {         long[] dp = new long[s.length() + 1];        dp[0] = 1;         // check the first character of the string        // if it is '*' then 9 ways         dp[1] = s.charAt(0) == '*'                    ? 9                    : s.charAt(0) == '0' ? 0 : 1;         // traverse the string        for (int i = 1; i < s.length(); i++) {             // If s[i] == '*' there can be            // 9 possible values of *            if (s.charAt(i) == '*') {                dp[i + 1] = 9 * dp[i];                 // If previous character is 1                // then words that can be formed                // are K(11), L(12), M(13), N(14)                // O(15), P(16), Q(17), R(18), S(19)                if (s.charAt(i - 1) == '1')                    dp[i + 1]                        = (dp[i + 1] + 9 * dp[i - 1]) % M;                 // If previous character is 2                // then the words that can be formed                // are U(21), V(22), W(23), X(24)Y(25), Z(26)                else if (s.charAt(i - 1) == '2')                    dp[i + 1]                        = (dp[i + 1] + 6 * dp[i - 1]) % M;                 // If the previous digit is * then                // all 15 2- digit characters can be                // formed                else if (s.charAt(i - 1) == '*')                    dp[i + 1]                        = (dp[i + 1] + 15 * dp[i - 1]) % M;            }            else {                // taking the value from previous step                dp[i + 1] = s.charAt(i) != '0' ? dp[i] : 0;                 // If previous character is 1 then                // the i-1th character and ith                // character can be decoded in                // a single character therefore,                // adding dp[i-1].                if (s.charAt(i - 1) == '1')                    dp[i + 1]                        = (dp[i + 1] + dp[i - 1])                          % M;                 // If previous character is 2                // and ith character is less than                // 6                // then the i-1th character and                // ith character can be decoded in                // a single character therefore,                // adding dp[i-1].                else if (s.charAt(i - 1) == '2'                         && s.charAt(i) <= '6')                    dp[i + 1]                        = (dp[i + 1] + dp[i - 1]) % M;                 // If previous character is * then                // it will contain the above 2 cases                else if (s.charAt(i - 1) == '*')                    dp[i + 1]                        = (dp[i + 1]                           + (s.charAt(i) <= '6' ? 2 : 1)                                 * dp[i - 1])                          % M;            }        }        return (int)dp[s.length()];    }    public static void main(String[] args)    {        String s = "12";        System.out.println(waysOfDecoding(s));    }}
# Python program for the above approachM = 1000000007def waysOfDecoding(s):     dp = [0]*(len(s)+1)    dp[0] = 1     # check the first character of the string    # if it is '*' then 9 ways    if s[0] == '*':        dp[1] = 9    elif s[0] == '0':        dp[1] = 0    else:        dp[1] = 1     # traverse the string    for i in range(len(s)):         # If s[i] == '*' there can be        # 9 possible values of *        if (s[i] == '*'):            dp[i + 1] = 9 * dp[i]             # If previous character is 1            # then words that can be formed            # are K(11), L(12), M(13), N(14)            # O(15), P(16), Q(17), R(18), S(19)            if (s[i - 1] == '1'):                dp[i + 1] = (dp[i + 1] + 9 * dp[i - 1]) % M             # If previous character is 2            # then the words that can be formed            # are U(21), V(22), W(23), X(24)Y(25), Z(26)            elif (s[i - 1] == '2'):                dp[i + 1] = (dp[i + 1] + 6 * dp[i - 1]) % M             # If the previous digit is * then            # all 15 2- digit characters can be            # formed            elif (s[i - 1] == '*'):                dp[i + 1] = (dp[i + 1] + 15 * dp[i - 1]) % M         else:            # taking the value from previous step            if s[i] != '0':                dp[i+1] = dp[i]            else:                dp[i+1] = 0             # If previous character is 1 then            # the i-1th character and ith            # character can be decoded in            # a single character therefore,            # adding dp[i-1].            if (s[i - 1] == '1'):                dp[i + 1] = (dp[i + 1] + dp[i - 1]) % M             # If previous character is 2            # and ith character is less than            # 6            # then the i-1th character and            # ith character can be decoded in            # a single character therefore,            # adding dp[i-1].            elif (s[i - 1] == '2'                  and s[i] <= '6'):                dp[i + 1] = (dp[i + 1] + dp[i - 1]) % M             # If previous character is * then            # it will contain the above 2 cases            elif (s[i - 1] == '*'):                if (s[i] <= '6'):                    dp[i + 1] = dp[i + 1] + 2 * dp[i - 1]                else:                    dp[i + 1] = dp[i + 1] + 1 * dp[i - 1]                 dp[i+1] = dp[i+1] % M     return dp[len(s)]  if __name__ == "__main__":     s = "12"    print(waysOfDecoding(s))     # This code is contributed by ukasp.
// C# program for the above approachusing System; class GFG{     static int M = 1000000007;static int waysOfDecoding(String s){    long[] dp = new long[s.Length + 1];    dp[0] = 1;     // Check the first character of the string    // if it is '*' then 9 ways    dp[1] = s[0] == '*' ? 9 : s[0] == '0' ? 0 : 1;     // Traverse the string    for(int i = 1; i < s.Length; i++)    {                 // If s[i] == '*' there can be        // 9 possible values of *        if (s[i] == '*')        {            dp[i + 1] = 9 * dp[i];             // If previous character is 1            // then words that can be formed            // are K(11), L(12), M(13), N(14)            // O(15), P(16), Q(17), R(18), S(19)            if (s[i - 1] == '1')                dp[i + 1] = (dp[i + 1] + 9 *                             dp[i - 1]) % M;             // If previous character is 2            // then the words that can be formed            // are U(21), V(22), W(23), X(24)Y(25),            // Z(26)            else if (s[i - 1] == '2')                dp[i + 1] = (dp[i + 1] + 6 *                             dp[i - 1]) % M;             // If the previous digit is * then            // all 15 2- digit characters can be            // formed            else if (s[i - 1] == '*')                dp[i + 1] = (dp[i + 1] + 15 *                             dp[i - 1]) % M;        }        else        {                         // Taking the value from previous step            dp[i + 1] = s[i] != '0' ? dp[i] : 0;             // If previous character is 1 then            // the i-1th character and ith            // character can be decoded in            // a single character therefore,            // adding dp[i-1].            if (s[i - 1] == '1')                dp[i + 1] = (dp[i + 1] + dp[i - 1]) % M;             // If previous character is 2            // and ith character is less than            // 6            // then the i-1th character and            // ith character can be decoded in            // a single character therefore,            // adding dp[i-1].            else if (s[i - 1] == '2' && s[i] <= '6')                dp[i + 1] = (dp[i + 1] + dp[i - 1]) % M;             // If previous character is * then            // it will contain the above 2 cases            else if (s[i - 1] == '*')                dp[i + 1] = (dp[i + 1] + (s[i] <= '6' ? 2 : 1) *                             dp[i - 1]) % M;        }    }    return (int)dp[s.Length];} // Driver codepublic static void Main(){    String s = "12";    Console.WriteLine(waysOfDecoding(s));}} // This code is contributed by rishavmahato348
<script>// Javascript program for the above approach let M = 1000000007;function waysOfDecoding(s){    let dp = new Array(s.length + 1);    for(let i=0;i<s.length+1;i++)        dp[i] = 0;        dp[0] = 1;          // check the first character of the string        // if it is '*' then 9 ways          dp[1] = s[0] == '*'                    ? 9                    : s[0] == '0' ? 0 : 1;          // traverse the string        for (let i = 1; i < s.length; i++) {              // If s[i] == '*' there can be            // 9 possible values of *            if (s[i] == '*') {                dp[i + 1] = 9 * dp[i];                  // If previous character is 1                // then words that can be formed                // are K(11), L(12), M(13), N(14)                // O(15), P(16), Q(17), R(18), S(19)                if (s[i-1] == '1')                    dp[i + 1]                        = (dp[i + 1] + 9 * dp[i - 1]) % M;                  // If previous character is 2                // then the words that can be formed                // are U(21), V(22), W(23), X(24)Y(25), Z(26)                else if (s[i-1] == '2')                    dp[i + 1]                        = (dp[i + 1] + 6 * dp[i - 1]) % M;                  // If the previous digit is * then                // all 15 2- digit characters can be                // formed                else if (s[i-1] == '*')                    dp[i + 1]                        = (dp[i + 1] + 15 * dp[i - 1]) % M;            }            else {                // taking the value from previous step                dp[i + 1] = s[i] != '0' ? dp[i] : 0;                  // If previous character is 1 then                // the i-1th character and ith                // character can be decoded in                // a single character therefore,                // adding dp[i-1].                if (s[i-1] == '1')                    dp[i + 1]                        = (dp[i + 1] + dp[i - 1])                          % M;                  // If previous character is 2                // and ith character is less than                // 6                // then the i-1th character and                // ith character can be decoded in                // a single character therefore,                // adding dp[i-1].                else if (s[i-1] == '2'                         && s[i] <= '6')                    dp[i + 1]                        = (dp[i + 1] + dp[i - 1]) % M;                  // If previous character is * then                // it will contain the above 2 cases                else if (s[i-1] == '*')                    dp[i + 1]                        = (dp[i + 1]                           + (s[i] <= '6' ? 2 : 1)                                 * dp[i - 1])                          % M;            }        }        return dp[s.length];} let s = "12";document.write(waysOfDecoding(s)); // This code is contributed by unknown2108</script>
2
Time complexity: O(n)Auxiliary Space: O(n)
Further optimization of space 
If the above code is observed carefully, it is observed that the value of dp[i] is found using dp[i-1] and dp[i-2]. So to optimize the space further, instead of creating an array of dp of length N, we can use three variables – second(stores the value of dp[i]), first(stores the value of dp[i-2]), and temp(stores the value of dp[i-1]). So after finding the value of the second(dp[i]), modify first = temp and temp = second and then calculate the value again of second(dp[i]) using the variable first and temp. 
C++
Java
C#
Javascript
// C++ program for the above approach#include <bits/stdc++.h>using namespace std; int M = 1000000007;int waysOfDecoding(string s){    long first = 1,    second = s[0] == '*' ? 9 : s[0] == '0' ? 0 : 1;         for(int i = 1; i < s.size(); i++)    {        long temp = second;         // If s[i] == '*' there can be        // 9 possible values of *        if (s[i] == '*')        {            second = 9 * second;                         // If previous character is 1            // then words that can be formed            // are K(11), L(12), M(13), N(14)            // O(15), P(16), Q(17), R(18), S(19)            if (s[i - 1] == '1')                second = (second + 9 * first) % M;             // If previous character is 2            // then the words that can be formed            // are U(21), V(22), W(23), X(24)Y(25), Z(26)            else if (s[i - 1] == '2')                second = (second + 6 * first) % M;             // If the previous digit is * then            // all 15 2- digit characters can be            // formed            else if (s[i - 1] == '*')                second = (second + 15 * first) % M;        }                 // If s[i] != '*'        else        {            second = s[i] != '0' ? second : 0;             // Adding first in second            // if s[i-1]=1            if (s[i - 1] == '1')                second = (second + first) % M;             // Adding first in second if            // s[i-1] == 2 and s[i]<='6'            else if (s[i - 1] == '2' && s[i] <= '6')                second = (second + first) % M;             // If s[i-1] == '*' the union            // of above 2 cases has to be done            else if (s[i - 1] == '*')                second = (second + (s[i] <= '6' ? 2 : 1) *                          first) % M;        }        first = temp;    }    return(int)second;} // Driver codeint main(){    string s = "*";    cout << waysOfDecoding(s);    return 0;} // This code is contributed by rishavmahato348
// Java program for the above approachimport java.io.*; class GFG {    static int M = 1000000007;    static int waysOfDecoding(String s)    {        long first = 1, second                        = s.charAt(0) == '*'                              ? 9                              : s.charAt(0) == '0' ? 0 : 1;        for (int i = 1; i < s.length(); i++) {            long temp = second;             // If s[i] == '*' there can be            // 9 possible values of *            if (s.charAt(i) == '*') {                second = 9 * second;                 // If previous character is 1                // then words that can be formed                // are K(11), L(12), M(13), N(14)                // O(15), P(16), Q(17), R(18), S(19)                if (s.charAt(i - 1) == '1')                    second = (second + 9 * first) % M;                 // If previous character is 2                // then the words that can be formed                // are U(21), V(22), W(23), X(24)Y(25), Z(26)                else if (s.charAt(i - 1) == '2')                    second = (second + 6 * first) % M;                 // If the previous digit is * then                // all 15 2- digit characters can be                // formed                else if (s.charAt(i - 1) == '*')                    second = (second + 15 * first) % M;            }            // If s[i] != '*'            else {                second = s.charAt(i) != '0' ? second : 0;                 // Adding first in second                // if s[i-1]=1                if (s.charAt(i - 1) == '1')                    second = (second + first) % M;                 // Adding first in second if                // s[i-1] == 2 and s[i]<='6'                else if (s.charAt(i - 1) == '2'                         && s.charAt(i) <= '6')                    second = (second + first) % M;                 // if s[i-1] == '*' the union                // of above 2 cases has to be done                else if (s.charAt(i - 1) == '*')                    second = (second                              + (s.charAt(i) <= '6' ? 2 : 1)                                    * first)                             % M;            }            first = temp;        }        return (int)second;    }    public static void main(String[] args)    {        String s = "*";        System.out.println(waysOfDecoding(s));    }}
// C# program for the above approachusing System; class GFG{     static int M = 1000000007; static int waysOfDecoding(string s){    long first = 1,    second = s[0] == '*' ? 9 : s[0] == '0' ? 0 : 1;    for(int i = 1; i < s.Length; i++)    {        long temp = second;         // If s[i] == '*' there can be        // 9 possible values of *        if (s[i] == '*')        {            second = 9 * second;             // If previous character is 1            // then words that can be formed            // are K(11), L(12), M(13), N(14)            // O(15), P(16), Q(17), R(18), S(19)            if (s[i - 1] == '1')                second = (second + 9 * first) % M;             // If previous character is 2            // then the words that can be formed            // are U(21), V(22), W(23), X(24)Y(25), Z(26)            else if (s[i - 1] == '2')                second = (second + 6 * first) % M;             // If the previous digit is * then            // all 15 2- digit characters can be            // formed            else if (s[i - 1] == '*')                second = (second + 15 * first) % M;        }                 // If s[i] != '*'        else        {            second = s[i] != '0' ? second : 0;             // Adding first in second            // if s[i-1]=1            if (s[i - 1] == '1')                second = (second + first) % M;             // Adding first in second if            // s[i-1] == 2 and s[i]<='6'            else if (s[i - 1] == '2' && s[i] <= '6')                second = (second + first) % M;             // if s[i-1] == '*' the union            // of above 2 cases has to be done            else if (s[i - 1] == '*')                second = (second + (s[i] <= '6' ? 2 : 1) *                          first) % M;        }        first = temp;    }    return (int)second;} // Driver codestatic public void Main(){    string s = "*";         Console.WriteLine(waysOfDecoding(s));}} // This code is contributed by patel2127
<script> // JavaScript program for the above approachlet M = 1000000007; function waysOfDecoding(s){    let first = 1,    second = s[0] == '*' ? 9 : s[0] == '0' ? 0 : 1;    for(let i = 1; i < s.length; i++)    {        let temp = second;         // If s[i] == '*' there can be        // 9 possible values of *        if (s[i] == '*')        {            second = 9 * second;             // If previous character is 1            // then words that can be formed            // are K(11), L(12), M(13), N(14)            // O(15), P(16), Q(17), R(18), S(19)            if (s[i - 1] == '1')                second = (second + 9 * first) % M;             // If previous character is 2            // then the words that can be formed            // are U(21), V(22), W(23), X(24)Y(25), Z(26)            else if (s[i - 1] == '2')                second = (second + 6 * first) % M;             // If the previous digit is * then            // all 15 2- digit characters can be            // formed            else if (s[i - 1] == '*')                second = (second + 15 * first) % M;        }                 // If s[i] != '*'        else        {            second = s[i] != '0' ? second : 0;             // Adding first in second            // if s[i-1]=1            if (s[i - 1] == '1')                second = (second + first) % M;             // Adding first in second if            // s[i-1] == 2 and s[i]<='6'            else if (s[i - 1] == '2' && s[i] <= '6')                second = (second + first) % M;             // if s[i-1] == '*' the union            // of above 2 cases has to be done            else if (s[i - 1] == '*')                second = (second + (s[i] <= '6' ? 2 : 1) *                          first) % M;        }        first = temp;    }    return second;} // Driver Codelet s = "*"; document.write(waysOfDecoding(s)); // This code is contributed by code_hunt </script>
9
Time complexity: O(n)Auxiliary Space: O(1)
 
mohit kumar 29
unknown2108
rishavmahato348
patel2127
code_hunt
ukasp
digit-DP
Facebook
interview-preparation
Dynamic Programming
Mathematical
Strings
Facebook
Strings
Dynamic Programming
Mathematical
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Comments
Old Comments
Optimal Substructure Property in Dynamic Programming | DP-2
Min Cost Path | DP-6
Maximum Subarray Sum using Divide and Conquer algorithm
Optimal Binary Search Tree | DP-24
Greedy approach vs Dynamic programming
Write a program to print all permutations of a given string
C++ Data Types
Set in C++ Standard Template Library (STL)
Program to find sum of elements in a given array
Modulo Operator (%) in C/C++ with Examples