C++ 0091.cpp
class Solution {
public:
int numDecodings(string s) {
map<int, int> dp;
dp[0] = 1;
for (int i = 0; i < s.size(); i++)
{
auto c = s[i];
if (c == '0')
{
if (i == 0 || s[i-1] == '0' || s[i-1] > '2')
return 0;
dp[i+1] += dp[i-1];
continue;
}
dp[i+1] += dp[i];
if (i == 0)
continue;
if (s[i-1] == '1')
{
dp[i+1] += dp[i-1];
continue;
}
if (s[i-1] == '2' && c >= '1' && c <= '6')
{
dp[i+1] += dp[i-1];
continue;
}
}
return dp[s.size()];
}
};
Difficulty
and has an acceptance rate of about 38% (4+ million submissions).
Typical patterns to solve this problem:
- Dynamic Programming
- String
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The provided implementation solves the classic decoding problem where a numeric string is interpreted as letters (1 → 'A', 2 → 'B', …, 26 → 'Z'). The method numDecodings computes the total number of valid ways to decode the input string s. It employs a dynamic-programming map dp where the key represents a position in the string (the number of characters processed) and the value stores the count of decoding ways up to that position. The algorithm iterates once over the characters, updating the map based on the current digit and its predecessor, and returns the final count for the full length of the string.
Key Steps
The initialization creates a base case for an empty prefix:
map<int, int> dp;
dp[0] = 1;
The main loop processes each character by index i:
for (int i = 0; i < s.size(); i++)
{
auto c = s[i];
// handling logic follows
}
When the current character is '0', the code checks whether it can form a valid two-digit number with the preceding character. If the preceding character is '1' or '2', the zero can be paired; otherwise the function returns 0 indicating an impossible decoding. The valid pairing contributes the count from two positions back:
if (c == '0')
{
if (i == 0 || s[i-1] == '0' || s[i-1] > '2')
return 0;
dp[i+1] += dp[i-1];
continue;
}
For non-zero digits, the count from the previous position is always added, reflecting the possibility of decoding the current digit as a standalone letter:
dp[i+1] += dp[i];
If the current index is not the first character, the algorithm examines the two-character window formed with the previous digit. When the previous digit is '1', any current digit creates a valid two-digit number (10-19), so the count from two positions back is added:
if (s[i-1] == '1')
{
dp[i+1] += dp[i-1];
continue;
}
When the previous digit is '2' and the current digit lies between '1' and '6', the pair forms a valid number (21-26). The same two-step contribution is applied:
if (s[i-1] == '2' && c >= '1' && c <= '6')
{
dp[i+1] += dp[i-1];
continue;
}
After the loop finishes, the result for the full string length is retrieved:
return dp[s.size()];
Edge Cases Handled
The code explicitly guards against leading zeros. If the first character is '0', the early return inside the zero-handling block triggers because i == 0. This prevents any decoding path from starting with an invalid digit.
When a '0' appears later in the string, the algorithm validates that it is part of a legal '10' or '20' pair. The condition s[i-1] == '0' || s[i-1] > '2' filters out illegal combinations such as '30' or '00'. In those scenarios the function returns 0, indicating that no decoding exists.
The two-digit validation for '2' ensures that only numbers up to 26 are considered. Digits '7' through '9' following a '2' are ignored for the two-digit contribution, preserving correctness for inputs like "27" where only the '2' and '7' can be decoded separately.
The continuation statements (continue) after each successful update prevent double counting within the same iteration, guaranteeing that each decoding path is accounted for exactly once per relevant condition.
Performance Optimizations
The implementation uses a map<int, int> to store intermediate results. Because the keys are sequential integers from 0 to s.size(), the map provides O(log n) access, but the constant factor remains low due to the small range of keys. This choice avoids the need for a separate vector allocation and leverages the map’s automatic handling of missing keys (default-initialized to zero), simplifying the update logic.
The algorithm processes the input string in a single pass, performing only a constant amount of work per character. Early termination occurs when an impossible '0' configuration is detected, allowing the function to exit without scanning the remainder of the string. This early exit reduces unnecessary work for invalid inputs.
By reusing the same dp map throughout the loop and only inserting entries for i+1, the code minimizes memory churn. No additional temporary structures are created inside the loop, keeping the runtime overhead minimal.
Overall, the solution balances clarity with efficient use of standard containers, delivering a linear-time decoding count while handling all special cases directly within the iteration.
ELI5 - Simplified Explanation
Imagine you are walking down a path made of number blocks, and your goal is to count how many different ways you can jump from the start to the end. Each number block acts like a letter, where one is A, two is B, and so on, all the way up to twenty-six being Z.
The code uses a special notebook to keep track of how many ways you have found to reach each step along the way. It starts by saying there is one way to be at the beginning before you even take a step.
As you step on each number, you look at the blocks ahead of you. If you see a single number, you can treat it as one letter, like picking up a single toy. If you look at the current block together with the one right before it, sometimes they make a bigger number that happens to be a letter too, like putting two puzzle pieces together to make a whole picture.
When the code sees a zero, it knows that zero cannot be a letter on its own, so it checks if the zero belongs to the number before it to make a ten or a twenty. If it cannot do that, it stops because there are no ways to finish the path.
For every other number, the code adds up all the different ways to get to that spot. It looks at the ways to reach the spot just before and adds them to its notebook. If a pair of numbers forms a letter between ten and twenty-six, it also includes the ways to reach the spot two steps back. By the time you reach the very end, the notebook tells you exactly how many different stories can be told using those numbers.
Code Quality
Complexity Analysis
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| author | solutions | |||||||||
| www | AlgoMonster | C++ | Python | Java | TS | |||||
| WalkCCC | C++ | Python | Java | |||||||
| leetcode.ca | C++ | Python | Java | C# | Go | TS | ||||
| LeetcodeHelp | C++ | Python | Java | |||||||
| MemoryLimitExceeded | C++ | Python | ||||||||
| Progiez | C++ | Python | Java | |||||||
| github | aylei | Rust | ||||||||
| begeekmyfriend | C | |||||||||
| BigEggStudy | C# | |||||||||
| black-shadows | C++ | |||||||||
| bujosa | Python | |||||||||
| charles-wangkai | Java | |||||||||
| chenyanzhe | C++ | |||||||||
| everthis | JS | |||||||||
| FelixFan1122 | C# | |||||||||
| fengvyi | C++ | |||||||||
| gaosq0604 | Python | |||||||||
| Garvit244 | Python | |||||||||
| gouthampradhan | Java | |||||||||
| halfrost | Go | |||||||||
| haoel | C++ | |||||||||
| JoshCrozier | JS | |||||||||
| keineahnung2345 | C++ | |||||||||
| lennylxx | C | |||||||||
| linouk23 | C++ | |||||||||
| lkwq007 | Python | |||||||||
| luangong | C++ | |||||||||
| lzl124631x | C++ | |||||||||
| MacJim | C++ | |||||||||
| qiyuangong | Python | |||||||||
| ruoru | Rust | |||||||||
| suxrobGM | C# | |||||||||
| trung-hn | Python | |||||||||
| varunu28 | Java | |||||||||
| vli02 | C | |||||||||
| vthwang | Java | |||||||||
| wisdompeak | C++ | |||||||||
| zfang399 | C++ | |||||||||
| AlgoMonster | C++, Python, Java, TypeScript | |||||||||
| WalkCCC | C++, Python, Java | |||||||||
| leetcode.ca | C++, C#, Go, Java, Python, TypeScript | |||||||||
| LeetcodeHelp | C++, Java, Python | |||||||||
| MemoryLimitExceeded | Python, C++ | |||||||||
| Progiez | C++, Java, Python | |||||||||
| aylei | Rust | |||||||||
| begeekmyfriend | C | |||||||||
| BigEggStudy | C# | |||||||||
| black-shadows | C++, C++ | |||||||||
| bujosa | Python | |||||||||
| charles-wangkai | Java | |||||||||
| chenyanzhe | C++ | |||||||||
| everthis | JavaScript | |||||||||
| FelixFan1122 | C# | |||||||||
| fengvyi | C++ | |||||||||
| gaosq0604 | Python | |||||||||
| Garvit244 | Python | |||||||||
| gouthampradhan | Java | |||||||||
| halfrost | Go | |||||||||
| haoel | C++ | |||||||||
| JoshCrozier | JavaScript | |||||||||
| keineahnung2345 | C++ | |||||||||
| lennylxx | C | |||||||||
| linouk23 | C++ | |||||||||
| lkwq007 | Python | |||||||||
| luangong | C++ | |||||||||
| lzl124631x | C++ | |||||||||
| MacJim | C++ | |||||||||
| qiyuangong | Python | |||||||||
| ruoru | Rust | |||||||||
| suxrobGM | C# | |||||||||
| trung-hn | Python | |||||||||
| varunu28 | Java | |||||||||
| vli02 | C | |||||||||
| vthwang | Java | |||||||||
| wisdompeak | C++ | |||||||||
| zfang399 | C++ |
Videos
Similar Problems
- problem 639: Decode Ways II
- problem 2266: Count Number of Texts
Solved On The Same Day
- problem 1: Two Sum
- problem 9: Palindrome Number
- problem 14: Longest Common Prefix
- problem 20: Valid Parentheses
- problem 26: Remove Duplicates from Sorted Array
- problem 27: Remove Element
- problem 83: Remove Duplicates from Sorted List
- problem 88: Merge Sorted Array
- problem 94: Binary Tree Inorder Traversal
- problem 100: Same Tree
- problem 104: Maximum Depth of Binary Tree
- problem 121: Best Time to Buy and Sell Stock
- problem 136: Single Number
- problem 141: Linked List Cycle
- problem 169: Majority Element
- problem 190: Reverse Bits
- problem 226: Invert Binary Tree
- problem 234: Palindrome Linked List
- problem 1114: Print in Order
- problem 3: Longest Substring Without Repeating Characters
- problem 7: Reverse Integer
- problem 8: String to Integer (atoi)
- problem 11: Container With Most Water
- problem 12: Integer to Roman
- problem 15: 3Sum
- problem 17: Letter Combinations of a Phone Number
- problem 22: Generate Parentheses
- problem 31: Next Permutation
- problem 36: Valid Sudoku
- problem 39: Combination Sum
- problem 40: Combination Sum II
- problem 45: Jump Game II
- problem 46: Permutations
- problem 47: Permutations II
- problem 48: Rotate Image
- problem 49: Group Anagrams
- problem 55: Jump Game
- problem 59: Spiral Matrix II
- problem 75: Sort Colors
- problem 77: Combinations
- problem 95: Unique Binary Search Trees II
- problem 96: Unique Binary Search Trees
- problem 134: Gas Station
- problem 164: Maximum Gap
- problem 189: Rotate Array
- problem 215: Kth Largest Element in an Array
- problem 238: Product of Array Except Self
- problem 287: Find the Duplicate Number
- problem 322: Coin Change
- problem 347: Top K Frequent Elements
- problem 377: Combination Sum IV
- problem 438: Find All Anagrams in a String
- problem 474: Ones and Zeroes
- problem 518: Coin Change II
- problem 526: Beautiful Arrangement
- problem 763: Partition Labels
- problem 983: Minimum Cost For Tickets
- problem 994: Rotting Oranges
- problem 1027: Longest Arithmetic Subsequence
- problem 2140: Solving Questions With Brainpower
- problem 2466: Count Ways To Build Good Strings
- problem 4: Median of Two Sorted Arrays
- problem 41: First Missing Positive
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality