From : https://leetcode.com/problems/decode-ways/
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"
(1
2) or "L"
(12).
The number of ways decoding "12"
is 2.
本题是斐波那契数列的变种 。
思路1:寻找最大的可以组合的子串,如“1217201211”中,最大的可以组合的子串是 “1217“,”20” 和 “1211”, 因为7和0后面是不可能有1~26的数字,所以这个串应该分成上面三个子串。然后所有组合的可能结果就是子串可能结果的乘积。 对于任意子串,其可能结果的组合是要么两个一起,要么一个一个的,所以是Fibonacci数列。
子串中,需要考虑特殊的 “110”,“10”等这样以0结尾的子串。0必须和前面一个字符组合,所以组合就减少了2个字符产生的结果。即: “10”如果两个都参与会有两种结果,实际1和0必须在一起,所以只有一种结果;“2210”如果4个都参与组合会有8中情况,但由于1必须和0组合,所以只有4-2=2个字符参与组合,故只有两种情况。
class Solution {
public:
vector<int> iniFibo(int n) {
vector<int> f(max(3, n+1));
f[0] = 0; f[1] = 1; f[2] = 2;
for(int i=3; i<=n; i++) f[i] = f[i-1]+f[i-2];
return f;
}
int numDecodings(string s) {
if(s.size() == 0 || s[0]=='0') return 0;
vector<int> fibs = iniFibo(s.size());
int len = s.size(), ans=1, last;
vector<bool> canCombi(len, false); // true表示i处可以和i+1处结合成一个新的字母,11可以是L
for(int i=0, len1=len-1; i<len1; i++){
if(s[i]!='1' && s[i]!='2' && s[i+1]=='0') return 0; // 只有1和2后面才能有0
if(s[i]=='1' || s[i]=='2'&&s[i+1]>='0'&&s[i+1]<='6') {
canCombi[i]=true;
}
}
for(int k=0; k<len; k++) {
last = k;
while(k<len && canCombi[k]) k++;
// s[k]=='0' 时候,要保证至少有一组,且由于0和前面一个要组合用,所以fibo应该减少2
int base = (s[k]=='0'?max(k-last-1, 1):k-last+1);
ans *= fibs[base];
}
return ans;
}
};
public class Solution {
public int numDecodings(String s) {
if (s == null || s.equals("") || s.charAt(0) == '0') {
return 0;
}
int[] fibo = initFibo(s.length());
boolean[] beJoined = new boolean[s.length()];
for (int i = 0; i < s.length() - 1; ++i) {
char a = s.charAt(i);
char b = s.charAt(i + 1);
if (b == '0' && a != '1' && a != '2') {
return 0;
}
if (a == '1' || a == '2' && b >= '0' && b <= '6') {
beJoined[i] = true;
}
}
int ans = 1;
for (int i = 0; i < beJoined.length; i++) {
int j = i;
while (i < beJoined.length && beJoined[i]) {
++i;
}
ans *= fibo[i - j + 1 - (s.charAt(i) == '0' ? 2 : 0)];
}
return ans;
}
private int[] initFibo(int n) {
int[] f = new int[n + 2];
f[0] = 1;
f[1] = 1;
for (int i = 2; i <= n; ++i) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
}
思路2: 每次对于当前的字符判断是否属于1-9(0肯定不行,因为0不在1-26中),如果属于,那么当前的字符可以被decode,并且和f[n-1]组合,f[n] += f[n-1]. 然后对于当前字符和前一个字符组成的字符串判断是否属于10-26,如果属于,那么这两个字符可以被decode,并且和f[n-2]组合,f[n] += f[n-2]
class Solution {
public:
int numDecodings(string s) {
if (s == "" || s[0]=='0') return 0;
vector<int> f(s.size());
f[0] = 1;
int i = 1;
for (; i < s.size(); i++){
f[i] = 0;
if('1'<=s[i] && s[i]<='9') f[i] += f[i-1];
string tmp(s, i-1, 2);
if("10" <= tmp && tmp <= "26")
if(i > 1) { f[i] += f[i-2];}
else{ f[i] += 1;}//i == 1,没有i - 2这个index
}
return f[i-1];
}
};
class Solution {
public:
int numDecodings(string s) {
if (s == "" || s[0]=='0') return 0;
int cur=1, pre1=1, pre2=1;
for (int i = 1; i < s.size(); i++){
if(s[i]=='0' && s[i-1]=='0') return 0;
cur = 0;
if('1'<=s[i] && s[i]<='9') cur += pre1;
string tmp(s, i-1, 2);
if("10" <= tmp && tmp <= "26")
if(i > 1) { cur += pre2;}
else{ cur += 1;}//i == 1,没有i - 2这个index
pre2 = pre1;
pre1 = cur;
}
return cur;
}
};
public class Solution {
public int numDecodings(String s) {
if(s == null || s.length()==0 || s.charAt(0) == '0') {
return 0;
}
int ans = 1;
int e1 = 1, e2 = 1;
for(int i=1; i<s.length(); ++i) {
ans = 0;
char c = s.charAt(i);
if(c>='1' && c<='9') {
ans += e1;
}
if(s.charAt(i-1)=='1' || s.charAt(i-1)=='2' && c<='6') {
ans += e2;
}
e2 = e1;
e1 = ans;
}
return ans;
}
}