对于题目的理解,其实也不难,我们并没有必要把数字真的转换成它要求的字母,只要得出有多少种分割方法就行了。
这种分割的问题也叫“隔板问题”——在数字之间的缝隙里插入隔板,看有多少种分法,是一类组合问题。这里由于受到26个字母的限制,只需要考虑分割之后,每两个“隔间”内有两个数字就可以了。也就是说,我们只需要考虑当前数字与它后面的数字的组合是不是在[10,25]内即可,这里之所以是边界是10,因为会出现01,02这种,这种不是有效的2位数,不能转化为对应的字母
这么想的话,实际上该问题已经有点像经典的“爬楼梯”问题了:一次上一阶或者两阶,总共有多少种走法。于是自然想到了用动态规划的解法,关键性的问题也就出现了:递推公式是什么?
首先我们一定能想到不复杂的情况:
只有一个数字时,不用切割,只有一种对映,dp[0] = 1; dp[1]的值是多少实际上取决于字符串str[1]和str[0]构成的数字是不是处于[0,25]区间内——是,则dp[1] = 2;否则dp[1] = 1. 这两条是最容易想到的,不过也确实为后面的条件判断打下基础了。接下来是数学中的归纳法,通俗说是找规律。以题目中的输入示例12258来看,dp[0] = 1, dp[1] = 2
dp[2] = 3((1,22), (12,2), (1,2,2)) dp[3] = 5 ((12,25), (1,2,25), (1,22,5), (12,2,5),(1,2,2,5)) dp[4]还是5,原因是str[4] = 8与str[3] = 5组合起来大于25,不能对映到一个字符,5和8之间必须切开,所以加上的这个数字8并不影响分割方式。 好了,上面的例子写完,如果还没找到规律可以接着往后写。对于dp问题敏感的选手应该已经想到需要用上dp[i], dp[i-1]这些元素来总结递推了:
代码语言:javascript复制2 <= i < len(str)
if str[i]和str[i-1]构成的数字 < 26:
dp[i] = dp[i-1] dp[i-2] #等于前两项之和
else:
dp[i] = dp[i-1] #不受影响直接等于前一项
代码
代码语言:javascript复制public class Solution46m {
public int translateNum(int num) {
String res = String.valueOf(num);
int[] dp = new int[res.length()];
if (res.length() < 2) {
return res.length();
}
dp[0] = 1;
int firstSum = Integer.valueOf(res.substring(0, 2));
dp[1] = firstSum >= 10 && firstSum <= 25 ? 2 : 1;
for (int i = 2; i < res.length(); i ) {
int sum = Integer.valueOf(res.substring(i - 1, i 1));
if (sum >= 10 && sum <= 25) {
dp[i] = dp[i - 1] dp[i - 2];
} else {
dp[i] = dp[i - 1];
}
}
return dp[res.length()-1];
}
}