There are two ways to solve this problem. One is to solve it front-back, the other is back-front.
I currently only wrote the back-front method:
Suppose we have such a string: a0, a1, a2.... ai, ai+1, ai+2...an-1
Suppose we have get that from an-1, an-2...ai+2, the ways of decoding such a sequence is dp[i+2]
There are three conditions to consider:
1: if ai == 1, we dont need to care about ai+1, the ways to decode is 1+ dp[i+2];
2: if ai == 2, ai+1 should be smaller than '6', the ways to decode is 1 + dp[i+2].
3: if ai == 2, ai+1 greater than '6', the ways to decode is dp[i+1].
Think that, decode a individual number is 1, but 0 has no way to decode, thus, 0.
So, we can have the dynamic function: dp[i] = dp[i] + dp[i+2] if(s[i] == 1 || s[i] == 2 && s[i + 1] < '6')
#include <string>
#include <vector>
#include <iostream>
using namespace std;
/*
A message containing letters from A-Z is being encoded to numbers using the following mapping:
'A' --> 1
'B' --> 2
....
'Z' --> 26
Given an encoded message containing digits, determine the total number of ways to decode it.
For example:
Given encoded message "12", it could be decoded as "AB" or "L".
The number of ways decoding "12" is 2.
*/
/*
Analyze:
Several example will make this question clear.
"102" --> "10", "2" -> total method is 1
"12" --> {"1", "2"}, "12" --> total method is 2.
"127" --> {"1", "2", "7"}, {"12", "7"} --> total method is 2
"126" --> {"1", "2", "6"}, {"12", "6"}, {"1", "26"}--> total method is 3.
There are several cases need special attentions.
1: "1"/"2" + "0", the char "0" can be interpreted as a decode way.
2: "2" + "7, 8, 9" can only be interpreted as one way.
*/
int numDecodings(string s) {
int n = s.size();
vector<int> dp(n+2, 1);
for(int i = s.size() - 1; i >= 0; --i) {
if(s[i] == 0) dp[i] = 0;
else dp[i] = dp[i + 1];
if(i + 1 < s.size() && (s[i] == '1' || (s[i] == '2' && s[i+1] <= '6'))) {
dp[i] += dp[i+2];
}
}
return dp[0];
}
int main(void) {
cout << "123" << " decode ways: "<< numDecodings("123") << endl;
cout << "102" << " decode ways: "<< numDecodings("102") << endl;
cout << "127" << " decode ways: "<< numDecodings("127") << endl;
}
To visualize the problem.... Everytime, we can decode two chars or one chars.
'0' is a special case to case, there are two cases that we might encounter '0'. However, no matter where we have 0, the corresponding decode way will 0.
#include <string>
#include <vector>
#include <iostream>
using namespace std;
// decode it from back to front.
int decodeWays(string str, int len) {
if(len == 0 || len == 1) return 1;
int num = 0;
if(str[len - 1] > '0') // this line should be commented out! for the case "10000000" == 1
num = decodeWays(str.substr(0, len - 1), len - 1);
if(str[len - 2] == '1' || ((str[len - 2] == '2') && str[len - 1] < '6'))
num += decodeWays(str.substr(0, len - 2), len - 2);
return num;
}
// dp
int decodeWaysII(string str) {
int n = str.size();
vector<int> dp(n + 1, 0);
dp[0] = dp[1] = 1;
for(int i = 2; i <= n; ++i) {
if(str[i - 1] > '0') {
dp[i] = dp[i-1];
}
if(str[i-2] == '1' || (str[i-2] == '2' && str[i-1] < '6'))
dp[i] += dp[i-2];
}
return dp[n];
}
// to simplify more
int decodeWaysIII(string str) {
int n = str.size();
if(n <= 1) return 1;
int prev = 1;
int curr = 1;
int sum = 0;
for(int i = 2; i <= n; ++i) {
if(str[i-1] == '0') prev = 0;
if(str[i-2] != '1' && !(str[i-2] == '2' && str[i-1] < '6'))
curr = 0;
sum = curr + prev;
prev = curr;
curr = sum;
}
return curr;
}
Another ways is more cool! and space efficient!
// In-place ways
int decodeWays(string str) {
if(str[0] == '0') return 0;
int prev = 0;
int curr = 1;
for(int i = 1; i <= str.size(); ++i) {
if(s[i-1] == '0') curr = 0;
if(i < 2 || !(s[i-2] == '1' || (s[i-2] == '2' && s[i-1] <= '6')))
prev = 0;
int tmp = curr;
curr = curr + prev;
prev = tmp;
}
return curr;
}
reference: http://www.acmerblog.com/leetcode-solution-decode-ways-6209.html
Another variation: Asked by Amazon, return all the decoded strings, better to use backtracking, the time complexity is T(n) = T(n-1) + T(n-2) Thus it is 2^N
#include <iostream>
#include <unordered_map>
#include <vector>
#include <string>
using namespace std;
void decodeWays(string str, int pos, string path, vector<string>& res, unordered_map<string, char>& dict) {
if(pos > str.size()) return;
if(pos == str.size()) {
res.push_back(path);
return;
}
for(int i = 1; i <= 2; ++i) {
string tmp = str.substr(pos, i);
if(dict.count(tmp)) {
decodeWays(str, pos + i, path + dict[tmp], res, dict);
}
}
}
vector<string> decodeWays(int input, unordered_map<string, char>& dict) {
string str = to_string(input);
if(str.size() == 0) return {};
vector<string> res;
string path = "";
decodeWays(str, 0, path, res, dict);
return res;
}
int main(void) {
unordered_map<string, char> dict {
{"1", 'A'},
{"2", 'B'},
{"3", 'C'},
{"4", 'D'},
{"5", 'E'},
{"6", 'F'},
{"7", 'G'},
{"8", 'H'},
{"9", 'I'},
{"10", 'J'},
{"11", 'K'},
{"12", 'M'},
{"13", 'N'},
{"14", 'O'},
{"15", 'P'},
{"16", 'Q'},
{"17", 'R'},
{"18", 'S'},
{"19", 'T'},
{"20", 'U'},
{"21", 'V'},
{"22", 'W'},
{"23", 'X'},
{"24", 'Y'},
{"25", 'Z'}};
vector<string> res = decodeWays(123, dict);
for(int i = 0; i < res.size(); ++i) cout << res[i] << endl;
}