Count Possible Decodings of a given Digit Sequence (original) (raw)
Last Updated : 22 Mar, 2025
Let 1 maps to 'A', 2 maps to 'B', ..., 26 to 'Z'. Given a **digit sequence, **count the number of possible decodings of the given digit sequence.
Consider the input string ****"123"**. There are three valid ways to decode it:
- ****"ABC"**: The grouping is (1, 2, 3) → ****'A', 'B', 'C'**
- ****"AW"**: The grouping is (1, 23) → ****'A', 'W'**
- ****"LC"**: The grouping is (12, 3) → ****'L', 'C'**
**Note: Groupings that contain invalid codes (e.g., "0" by itself or numbers greater than "26") are not allowed.
For instance, the string ****"230"** is **invalid because ****"0"** cannot stand alone, and ****"30" is greater than "26",** so it cannot represent any letter. The task is to find the **total number of valid ways to decode a given string.
**Examples:
**Input: digits = "121"
**Output: 3
**Explanation: The possible decodings are "ABA", "AU", "LA"**Input: digits = "1234"
**Output: 3
**Explanation: The possible decodings are "ABCD", "LCD", "AWD"
Table of Content
- [Naive Approach] Using Recursion - O(2^n) Time and O(n) Space
- [Better Approach 1] Using Top-Down DP (Memoization) - O(n) Time and O(n) Space
- [Better Approach 2] Using Bottom-Up DP (Tabulation) - O(n) Time and O(n) Space
- [Expected Approach] Using Space Optimised DP - O(n) Time and O(1) Space
[Naive Approach] Using Recursion - O(2^n) Time and O(n) Space
For the **recursive approach to count decoding ways, there will be two cases:
- If the **current digit is not '0', the problem reduces to solving for the **remaining digits starting from the **next index.
- If the two digits form a valid number between 10 and 26, the problem reduces to solving for the digits starting two positions ahead.
The recurrence relation will look like this:
- decodeHelper(digits, index) = decodeHelper(digits, index + 1) + decodeHelper(digits, index + 2)
**Base Case: if index >= digits.length()
- decodeHelper(digits, index) = 1
C++ `
// C++ program to count decoding ways of a digit string // using recursion. #include <bits/stdc++.h> using namespace std;
// Helper function to recursively calculate decoding ways. int decodeHelper(string &digits, int index) {
int n = digits.length();
// Base case: If we reach the end of the string,
// return 1 as it signifies a valid decoding.
if (index >= n)
{
return 1;
}
int ways = 0;
// Single-digit decoding: check if current digit is not '0'.
if (digits[index] != '0')
{
ways = decodeHelper(digits, index + 1);
}
// Two-digit decoding: check if next two digits are valid.
if ((index + 1 < n) && ((digits[index] == '1' && digits[index + 1] <= '9') ||
(digits[index] == '2' && digits[index + 1] <= '6')))
{
ways += decodeHelper(digits, index + 2);
}
return ways;
}
// Function to count decoding ways for the // entire string. int countWays(string &digits) {
return decodeHelper(digits, 0);
}
int main() {
string digits = "121";
cout << countWays(digits) << endl;
return 0;
}
Java
// Java program to count decoding ways of a digit string // using recursion. import java.util.*;
class GfG {
// Helper function to recursively calculate decoding
// ways.
static int decodeHelper(String digits, int index)
{
int n = digits.length();
// Base case: If we reach the end of the string,
// return 1 as it signifies a valid decoding.
if (index >= n) {
return 1;
}
int ways = 0;
// Single-digit decoding: check if current digit is
// not '0'.
if (digits.charAt(index) != '0') {
ways = decodeHelper(digits, index + 1);
}
// Two-digit decoding: check if next two digits are
// valid.
if ((index + 1 < n)
&& ((digits.charAt(index) == '1'
&& digits.charAt(index + 1) <= '9')
|| (digits.charAt(index) == '2'
&& digits.charAt(index + 1) <= '6'))) {
ways += decodeHelper(digits, index + 2);
}
return ways;
}
// Function to count decoding ways for the
// entire string.
static int countWays(String digits)
{
return decodeHelper(digits, 0);
}
public static void main(String[] args)
{
String digits = "121";
System.out.println(countWays(digits));
}
}
Python
Python program to count decoding ways of a digit string
using recursion.
Helper function to recursively calculate decoding ways
def decodeHelper(digits, index): n = len(digits)
# Base case: If we reach the end of the string,
# return 1 as it signifies a valid decoding.
if index >= n:
return 1
ways = 0
# Single-digit decoding: check if current digit is not '0'.
if digits[index] != '0':
ways = decodeHelper(digits, index + 1)
# Two-digit decoding: check if next two digits are valid.
if (index + 1 < n and
((digits[index] == '1' and digits[index + 1] <= '9') or
(digits[index] == '2' and digits[index + 1] <= '6'))):
ways += decodeHelper(digits, index + 2)
return ways
Function to count decoding ways for the entire string
def countWays(digits): return decodeHelper(digits, 0)
if name == "main":
digits = "121"
print(countWays(digits))
C#
// C# program to count decoding ways of a digit string // using recursion. using System;
class GfG {
// Helper function to recursively calculate decoding
// ways.
static int DecodeHelper(string digits, int index)
{
int n = digits.Length;
// Base case: If we reach the end of the string,
// return 1 as it signifies a valid decoding.
if (index >= n) {
return 1;
}
int ways = 0;
// Single-digit decoding: check if current digit is
// not '0'.
if (digits[index] != '0') {
ways = DecodeHelper(digits, index + 1);
}
// Two-digit decoding: check if next two digits are
// valid.
if ((index + 1 < n)
&& ((digits[index] == '1'
&& digits[index + 1] <= '9')
|| (digits[index] == '2'
&& digits[index + 1] <= '6'))) {
ways += DecodeHelper(digits, index + 2);
}
return ways;
}
// Function to count decoding ways for the entire
// string.
static int countWays(string digits)
{
return DecodeHelper(digits, 0);
}
static void Main()
{
string digits = "121";
Console.WriteLine(countWays(digits));
}
}
JavaScript
// JavaScript program to count decoding ways of a digit // string using recursion.
function decodeHelper(digits, index) { const n = digits.length;
// Base case: If we reach the end of the string,
// return 1 as it signifies a valid decoding.
if (index >= n) {
return 1;
}
let ways = 0;
// Single-digit decoding: check if current digit is not
// '0'.
if (digits[index] !== "0") {
ways = decodeHelper(digits, index + 1);
}
// Two-digit decoding: check if next two digits are
// valid.
if (index + 1 < n
&& ((digits[index] === "1"
&& digits[index + 1] <= "9")
|| (digits[index] === "2"
&& digits[index + 1] <= "6"))) {
ways += decodeHelper(digits, index + 2);
}
return ways;
}
// Function to count decoding ways for the entire string function countWays(digits) { return decodeHelper(digits, 0); }
const digits = "121"; console.log(countWays(digits));
`
[Better Approach 1] Using Top-Down DP (Memoization****) -** O(n) **Time and O(n) Space
**1. Optimal Substructure: The solution to the problem can be broken down into smaller subproblems:
Mathematically, the recurrence relations are:
**if digits[index] != '0':
decodeHelper(digits, index) = decodeHelper(digits, index + 1)**if digits[index] and digits[index + 1] form a valid number :
decodeHelper(digits, index) += decodeHelper(digits, index + 2)**2. Overlapping Subproblems: The same subproblems are recalculated **multiple times, like computing the number of decodings from index i repeatedly. This overlap can be avoided using memoization to store already computed results.
memo[index] = decodeHelper(digits, index + 1) if digits[index] != '0'
memo[index] += decodeHelper(digits, index + 2) if digits[index] and digits[index + 1]
C++ `
// C++ program to count decoding ways of a digit string // using Memoization #include <bits/stdc++.h> using namespace std;
// Helper function to recursively calculate decoding ways // with memoization. int decodeHelper(string &digits, int index, vector &memo) {
int n = digits.length();
// Base case: If we reach the end of the string,
// return 1 as it signifies a valid decoding.
if (index >= n)
{
return 1;
}
// If the value is already calculated for this
// index, return it.
if (memo[index] != -1)
{
return memo[index];
}
int ways = 0;
// Single-digit decoding: check if current digit is not '0'.
if (digits[index] != '0')
{
ways = decodeHelper(digits, index + 1, memo);
}
// Two-digit decoding: check if next two digits are valid.
if ((index + 1 < n) && ((digits[index] == '1' && digits[index + 1] <= '9') ||
(digits[index] == '2' && digits[index + 1] <= '6')))
{
ways += decodeHelper(digits, index + 2, memo);
}
// Memoize the result for the current index.
memo[index] = ways;
return ways;
}
// Function to count decoding ways for the entire string. int countWays(string &digits) { int n = digits.length();
// Create a memoization vector initialized to -1.
vector<int> memo(n, -1);
return decodeHelper(digits, 0, memo);
}
int main() {
string digits = "121";
cout << countWays(digits) << endl;
return 0;
}
Java
// Java program to count decoding ways of a digit string // using recursion with memoization. import java.util.*;
class GfG {
// Helper function to recursively calculate decoding
// ways with memoization.
static int decodeHelper(String digits, int index,
int[] memo)
{
int n = digits.length();
// Base case: If we reach the end of the string,
// return 1 as it signifies a valid decoding.
if (index >= n) {
return 1;
}
// If the value is already calculated for this
// index, return it.
if (memo[index] != -1) {
return memo[index];
}
int ways = 0;
// Single-digit decoding: check if current digit is
// not '0'.
if (digits.charAt(index) != '0') {
ways = decodeHelper(digits, index + 1, memo);
}
// Two-digit decoding: check if next two digits are
// valid.
if ((index + 1 < n)
&& ((digits.charAt(index) == '1'
&& digits.charAt(index + 1) <= '9')
|| (digits.charAt(index) == '2'
&& digits.charAt(index + 1) <= '6'))) {
ways += decodeHelper(digits, index + 2, memo);
}
// Memoize the result for the current index.
memo[index] = ways;
return ways;
}
// Function to count decoding ways for the entire
// string.
static int countWays(String digits)
{
int n = digits.length();
// Create a memoization array initialized to -1.
int[] memo = new int[n];
Arrays.fill(memo, -1);
return decodeHelper(digits, 0, memo);
}
public static void main(String[] args)
{
String digits = "121";
System.out.println(countWays(digits));
}
}
Python
Python program to count decoding ways of a digit string
using recursion with memoization.
Helper function to recursively calculate decoding ways
def decodeHelper(digits, index, memo): n = len(digits)
# Base case: If we reach the end of the string,
# return 1 as it signifies a valid decoding.
if index >= n:
return 1
# If the result for this index is already
# calculated, return it.
if memo[index] != -1:
return memo[index]
ways = 0
# Single-digit decoding: check if current digit is not '0'.
if digits[index] != '0':
ways = decodeHelper(digits, index + 1, memo)
# Two-digit decoding: check if next two digits are valid.
if (index + 1 < n and
((digits[index] == '1' and digits[index + 1] <= '9') or
(digits[index] == '2' and digits[index + 1] <= '6'))):
ways += decodeHelper(digits, index + 2, memo)
# Memoize the result for the current index.
memo[index] = ways
return ways
Function to count decoding ways for the entire string
def countWays(digits): n = len(digits)
# Create a memoization list initialized to -1.
memo = [-1] * n
return decodeHelper(digits, 0, memo)
if name == "main":
digits = "121"
print(countWays(digits))
C#
// C# program to count decoding ways of a digit string // using recursion with memoization. using System;
class GfG {
// Helper function to recursively calculate
// decoding ways.
static int DecodeHelper(string digits, int index,
int[] memo)
{
int n = digits.Length;
// Base case: If we reach the end of the string,
// return 1 as it signifies a valid decoding.
if (index >= n) {
return 1;
}
// If result for this index is already computed,
// return it.
if (memo[index] != -1) {
return memo[index];
}
int ways = 0;
// Single-digit decoding: check if current
// digit is not '0'.
if (digits[index] != '0') {
ways = DecodeHelper(digits, index + 1, memo);
}
// Two-digit decoding: check if next two
// digits are valid.
if ((index + 1 < n)
&& ((digits[index] == '1'
&& digits[index + 1] <= '9')
|| (digits[index] == '2'
&& digits[index + 1] <= '6'))) {
ways += DecodeHelper(digits, index + 2, memo);
}
// Store the result in memo and return it.
memo[index] = ways;
return ways;
}
// Function to count decoding ways for the entire
// string.
static int countWays(string digits)
{
int n = digits.Length;
// Memoization array initialized to -1.
int[] memo = new int[n];
Array.Fill(memo, -1);
return DecodeHelper(digits, 0, memo);
}
static void Main()
{
string digits = "121";
Console.WriteLine(countWays(digits));
}
}
JavaScript
// JavaScript program to count decoding ways of a digit // string using recursion with memoization.
function decodeHelper(digits, index, memo) { const n = digits.length;
// Base case: If we reach the end of the string,
// return 1 as it signifies a valid decoding.
if (index >= n) {
return 1;
}
// If result for this index is already computed, return
// it.
if (memo[index] !== -1) {
return memo[index];
}
let ways = 0;
// Single-digit decoding: check if current digit is not
// '0'.
if (digits[index] !== "0") {
ways = decodeHelper(digits, index + 1, memo);
}
// Two-digit decoding: check if next two digits are
// valid.
if (index + 1 < n
&& ((digits[index] === "1"
&& digits[index + 1] <= "9")
|| (digits[index] === "2"
&& digits[index + 1] <= "6"))) {
ways += decodeHelper(digits, index + 2, memo);
}
// Store the result in memo and return it.
memo[index] = ways;
return ways;
}
// Function to count decoding ways for the entire string. function countWays(digits) { const n = digits.length;
// Memoization array initialized to -1.
const memo = new Array(n).fill(-1);
return decodeHelper(digits, 0, memo);
}
const digits = "121"; console.log(countWays(digits));
`
[Better Approach 2] Using Bottom-Up DP (Tabulation) -O(n) **Time and O(n) Space
The approach here is similar to the **recursive method, but instead of breaking down the problem recursively, we solve it iteratively in a bottom-up manner using dynamic programming. We will create a 1D array dp of size (n + 1), where dp[i], represents the number of ways to decode the **substring starting from **index i of the string **digits[].
**Dynamic Programming Relation:
If the current digit is not '0', it can be **decoded as a single digit, so we update the dp[i] as:
- **dp[i] = dp[i + 1]
If the next two digits form a valid number between **10 and 26, then we also consider decoding the current and next digit together:
- **dp[i] += dp[i + 2]
**Base Case: dp[n] = 1, where an **empty string has one valid decoding.
C++ `
// C++ program to count decoding ways of a digit string // using Tabulation #include <bits/stdc++.h> using namespace std;
// Function to count decoding ways for the entire string. int countWays(string &digits) { int n = digits.length();
// Create a dp array initialized to 0, with size n + 1.
vector<int> dp(n + 1, 0);
// Base case: An empty string has one valid decoding.
dp[n] = 1;
// Iterate from the end of the string to the beginning.
for (int i = n - 1; i >= 0; i--)
{
// Single-digit decoding: check if current
// digit is not '0'.
if (digits[i] != '0')
{
dp[i] = dp[i + 1];
}
// Two-digit decoding: check if next two digits are valid.
if ((i + 1 < n) &&
((digits[i] == '1' && digits[i + 1] <= '9') || (digits[i] == '2' && digits[i + 1] <= '6')))
{
dp[i] += dp[i + 2];
}
}
return dp[0];
}
int main() {
string digits = "121";
cout << countWays(digits) << endl;
return 0;
}
Java
// Java program to count decoding ways of a digit string // using Tabulation import java.util.ArrayList;
class GfG {
// Function to count decoding ways for the entire
// string.
public static int countWays(String digits)
{
int n = digits.length();
// Create a dp list initialized to 0, with size n
// + 1.
ArrayList<Integer> dp = new ArrayList<>();
for (int i = 0; i <= n; i++) {
dp.add(0);
}
// Base case: An empty string has one valid
// decoding.
dp.set(n, 1);
// Iterate from the end of the string to the
// beginning.
for (int i = n - 1; i >= 0; i--) {
// Single-digit decoding: check if current
// digit is not '0'.
if (digits.charAt(i) != '0') {
dp.set(i, dp.get(i + 1));
}
// Two-digit decoding: check if next two digits
// are valid.
if ((i + 1 < n)
&& ((digits.charAt(i) == '1'
&& digits.charAt(i + 1) <= '9')
|| (digits.charAt(i) == '2'
&& digits.charAt(i + 1) <= '6'))) {
dp.set(i, dp.get(i) + dp.get(i + 2));
}
}
return dp.get(0);
}
public static void main(String[] args)
{
String digits = "121";
System.out.println(countWays(digits));
}
}
Python
Python program to count decoding ways of a digit string
using Tabulation
Function to count decoding ways for the entire string.
def countWays(digits): n = len(digits)
# Create a dp list initialized to 0, with size n + 1.
dp = [0] * (n + 1)
# Base case: An empty string has one valid decoding.
dp[n] = 1
# Iterate from the end of the string to the beginning.
for i in range(n - 1, -1, -1):
# Single-digit decoding: check if current
# digit is not '0'.
if digits[i] != '0':
dp[i] = dp[i + 1]
# Two-digit decoding: check if next two digits are valid.
if (i + 1 < n and
((digits[i] == '1' and digits[i + 1] <= '9') or
(digits[i] == '2' and digits[i + 1] <= '6'))):
dp[i] += dp[i + 2]
return dp[0]
if name == "main":
digits = "121"
print(countWays(digits))
C#
// C# program to count decoding ways of a digit string // using Tabulation using System; using System.Collections.Generic;
class GfG {
// Function to count decoding ways for the entire
// string.
static int countWays(string digits)
{
int n = digits.Length;
// Create a dp list initialized to 0, with size n
// + 1.
List<int> dp = new List<int>(new int[n + 1]);
// Base case: An empty string has one valid
// decoding.
dp[n] = 1;
// Iterate from the end of the string to the
// beginning.
for (int i = n - 1; i >= 0; i--) {
// Single-digit decoding: check if current
// digit is not '0'.
if (digits[i] != '0') {
dp[i] = dp[i + 1];
}
// Two-digit decoding: check if next two digits
// are valid.
if ((i + 1 < n)
&& ((digits[i] == '1'
&& digits[i + 1] <= '9')
|| (digits[i] == '2'
&& digits[i + 1] <= '6'))) {
dp[i] += dp[i + 2];
}
}
return dp[0];
}
static void Main()
{
string digits = "121";
Console.WriteLine(countWays(digits));
}
}
JavaScript
// Javascript program to count decoding ways of // a digit string using Tabulation
// Function to count decoding ways for the entire string. function countWays(digits) { const n = digits.length;
// Create a dp array initialized to 0, with size n + 1.
const dp = Array(n + 1).fill(0);
// Base case: An empty string has one valid decoding.
dp[n] = 1;
// Iterate from the end of the string to the beginning.
for (let i = n - 1; i >= 0; i--) {
// Single-digit decoding: check if current
// digit is not '0'.
if (digits[i] !== "0") {
dp[i] = dp[i + 1];
}
// Two-digit decoding: check if next two digits are
// valid.
if (i + 1 < n
&& ((digits[i] === "1" && digits[i + 1] <= "9")
|| (digits[i] === "2"
&& digits[i + 1] <= "6"))) {
dp[i] += dp[i + 2];
}
}
return dp[0];
}
const digits = "121";
console.log(countWays(digits));
`
[Expected Approach] Using Space Optimised DP -O(n) **Time and O(1) Space
In **previous approach of dynamic programming we have derive the relation between states as given below:
- **dp[i] = dp[i + 1]
- **dp[i] = **dp[i]+ dp[i + 2]
If we observe that for calculating **current dp[i] state we only need previous two values dp[i+1] and dp[i]+dp[i+2]. There is no need to store all the **previous states.
C++ `
#include <bits/stdc++.h> using namespace std;
// Function to count decoding ways for the // entire string with optimized space. int countWays(string digits) { int n = digits.length();
// If the string is empty or starts with '0',
// there are no valid decodings.
if (n == 0 || digits[0] == '0')
{
return 0;
}
// two variables to store the previous two results.
int prev1 = 1, prev2 = 0;
for (int i = 1; i <= n; ++i)
{
int current = 0;
// Check for valid single-digit decoding
if (digits[i - 1] != '0')
{
current += prev1;
}
// Check for valid two-digit decoding
// (previous digit and current digit form a
// valid number between 10 and 26)
if (i > 1)
{
int twoDigit = (digits[i - 2] - '0') * 10 + (digits[i - 1] - '0');
if (twoDigit >= 10 && twoDigit <= 26)
{
current += prev2;
}
}
// Update prev1 and prev2 for the next iteration.
prev2 = prev1;
prev1 = current;
}
return prev1;
}
int main() { string digits = "121"; cout << countWays(digits) << endl; return 0; }
Java
// Function to count decoding ways for the entire string // with optimized space. class GfG { static int countWays(String digits) { int n = digits.length();
// If the string is empty or starts with '0',
// there are no valid decodings.
if (n == 0 || digits.charAt(0) == '0') {
return 0;
}
// two variables to store the previous two results.
int prev1 = 1, prev2 = 0;
for (int i = 1; i <= n; ++i) {
int current = 0;
// Check for valid single-digit decoding
if (digits.charAt(i - 1) != '0') {
current += prev1;
}
// Check for valid two-digit decoding
// (previous digit and current digit form
// a valid number between 10 and 26)
if (i > 1) {
int twoDigit
= (digits.charAt(i - 2) - '0') * 10
+ (digits.charAt(i - 1) - '0');
if (twoDigit >= 10 && twoDigit <= 26) {
current += prev2;
}
}
// Update prev1 and prev2 for the next
// iteration.
prev2 = prev1;
prev1 = current;
}
return prev1;
}
public static void main(String[] args)
{
String digits = "121";
System.out.println(countWays(digits));
}
}
Python
def countWays(digits): n = len(digits)
# If the string is empty or starts with '0',
# there are no valid decodings.
if n == 0 or digits[0] == '0':
return 0
# two variables to store the previous two results.
prev1, prev2 = 1, 0
for i in range(1, n + 1):
current = 0
# Check for valid single-digit decoding
if digits[i - 1] != '0':
current += prev1
# Check for valid two-digit decoding
# (previous digit and current digit form a valid number between 10 and 26)
if i > 1:
two_digit = (int(digits[i - 2]) * 10 + int(digits[i - 1]))
if 10 <= two_digit <= 26:
current += prev2
# Update prev1 and prev2 for the next iteration.
prev2 = prev1
prev1 = current
return prev1
if name == 'main': digits = '121' print(countWays(digits))
C#
using System;
class GfG { static int countWays(string digits) { int n = digits.Length;
// If the string is empty or starts with '0',
// there are no valid decodings.
if (n == 0 || digits[0] == '0') {
return 0;
}
// two variables to store the previous two results.
int prev1 = 1, prev2 = 0;
for (int i = 1; i <= n; ++i) {
int current = 0;
// Check for valid single-digit decoding
if (digits[i - 1] != '0') {
current += prev1;
}
// Check for valid two-digit decoding
// (previous digit and current digit form a
// valid number between 10 and 26)
if (i > 1) {
int twoDigit = (digits[i - 2] - '0') * 10
+ (digits[i - 1] - '0');
if (twoDigit >= 10 && twoDigit <= 26) {
current += prev2;
}
}
// Update prev1 and prev2 for the next
// iteration.
prev2 = prev1;
prev1 = current;
}
return prev1;
}
public static void Main()
{
string digits = "121";
Console.WriteLine(countWays(digits));
}
}
JavaScript
function countWays(digits) { const n = digits.length;
// If the string is empty or starts with '0',
// there are no valid decodings.
if (n === 0 || digits[0] === "0") {
return 0;
}
// two variables to store the previous two results.
let prev1 = 1, prev2 = 0;
for (let i = 1; i <= n; ++i) {
let current = 0;
// Check for valid single-digit decoding
if (digits[i - 1] !== "0") {
current += prev1;
}
// Check for valid two-digit decoding
// (previous digit and current digit form a valid
// number between 10 and 26)
if (i > 1) {
const twoDigit = (parseInt(digits[i - 2]) * 10
+ parseInt(digits[i - 1]));
if (twoDigit >= 10 && twoDigit <= 26) {
current += prev2;
}
}
// Update prev1 and prev2 for the next iteration.
prev2 = prev1;
prev1 = current;
}
return prev1;
}
const digits = "121"; console.log(countWays(digits));
`