数字字符串转换为字母组合的种数
数字字符串转换为字母组合的种数
《程序员代码面试指南》第71题 P238 难度:尉★★☆☆
这题不算很难,很快想出来,一个for循环了事,不过有些难理解。
我的思路是,假设数字字符串为“XXX...XXX”,那么“XXX...XXX”转换为字母组合的总数(记为p(N)),取决于倒数第一和第二个X。
- 如果倒数第一和第二个X能组合成1~26的二位数且倒数第二个X不为0,并且倒数第一个X不为0,那么p(N)=p(N-1)+p(N-2);
- 如果倒数第一和第二个X能组合成1~26的二位数且倒数第二个X不为0,但倒数第一个X为0,那么p(N)=p(N-2);
- 如果倒数第一和第二个X不能组合成1~26的二位数或倒数第二个X为0,但倒数第一个X不为0,那么p(N)=p(N-1);
- 剩下的情况就是倒数第一和第二个X都为0,或者倒数第一和第二个X不能组合成1~26的二位数且倒数第一个X为0,那么p(N)=0。
以上概括就是:
- 倒数第二个X不为0且能和倒数第一个X组合成1~26的二位数,p(N)+=p(N-2);
- 倒数第一个X不为0,p(N)+=p(N-1);
把N换成i,使其具有一般性。按照这种思想从左向右逐个遍历,就能得出最终的结果。
我的代码如下:
public int counts(char[] num) {
if(num == null || num.length == 0 || num[0] == '0')
return 0;
int last2 = 1, last1 = 1;
int sum = 0, tmp = 0;
for(int i=1; i<num.length; i++) {
sum = 0;
tmp = (num[i-1]-'0') * 10 + (num[i]-'0');
if(num[i-1] != '0' && tmp <= 26)
sum += last2;
if(num[i] != '0')
sum += last1;
last2 = last1;
last1 = sum;
}
return sum;
}
其中last2的初始值要设对(为1而不为0),可以通过长度为2的字符串来验证。
至于书上的思路(从暴力递归到优化),和我大致一样,只不过书上是从右向左遍历,而我是从左向右遍历。具体可以参照书P238-240,这里不再赘述。(我感觉我的思路比书上的好理解(⊙o⊙)…)
书P240还提到,本题是不能像斐波那契数列那题优化成矩阵乘法的,因为斐波那契数列是严格的f(i)=f(i-1)+f(i-2)。而本题的表达式是有状态转移的,如上所述,p(i)的值取决于左边两个数字,不是严格的p(i)=p(i-1)+p(i-2)。如果本题改成只由1和2字符组成,如“12121121212122”,那么此时一定有p(i)=p(i-1)+p(i-2),就可以使用矩阵乘法了。总之,可以使用矩阵乘法的前提是递归表达式不会发生转移。
最后贴出书上的代码:
public int num2(String str) {
if (str == null || str.equals("")) {
return 0;
}
char[] chs = str.toCharArray();
int cur = chs[chs.length - 1] == '0' ? 0 : 1;
int next = 1;
int tmp = 0;
for (int i = chs.length - 2; i >= 0; i--) {
if (chs[i] == '0') {
next = cur;
cur = 0;
} else {
tmp = cur;
if ((chs[i] - '0') * 10 + chs[i + 1] - '0' < 27) {
cur += next;
}
next = tmp;
}
}
return cur;
}

浙公网安备 33010602011771号