【DP】LeetCode 剑指 Offer 46. 把数字翻译成字符串
题目链接
思路
这个问题与 dp 中的经典问题“跳台阶”问题十分类似,在跳台阶问题中我们是选择跳一个台阶或者两个台阶,而在这个问题中我们是选择再统计一个字符还是再统计两个字符。即我们在遍历到第 \(i\) 个字符的时候,可以把它当做前面 \(i-1\) 个字符接上一个字符或者是前面 \(i-2\) 个字符接上两个字符。
举个栗子:
比如有一串数字为
12212
。
当我们遍历到1221
时,我们可以看做两种情况:
122
的后面接上了1
12
的后面接上了21
所以1221
的映射方法应该是122
的映射方法数加上12
的映射方法数,所以状态转移方程应该包含 \(dp[i]=dp[i-1]+dp[i-2]\)。
这里我们把末尾两字符的值记为 \(value\)。
只不过在跳台阶问题中我们无论怎样都能跳两个台阶,在该问题中需要满足 \(10 \leq value \leq 25\) 才能满足这两个字符能映射到字母,所以取 \(dp[i-2]\) 的值是有条件的。如果不满足条件,说明这种分割方法中的 \(value\) 没法映射到对应字母,即应该舍去 \(dp[i-2]\),此时状态方程变为 \(dp[i]=dp[i-1]\),即此时没法视为在 \(i-2\) 个字符的基础上接两个字符。
举个栗子:
比如有一串数字为
12345
当我们遍历到1234
时,我们可以看做两种情况:
123
的后面接上了4
12
的后面接上了34
但是
34
已经超过了25
,所以它没有映射的字母,需要把这种情况舍去。
综上所述,状态转移方程应该为:
\[dp[i]=\left\{
\begin{aligned}
& dp[i-1], & value <10, \\
& dp[i-1] + dp[i-2],& 10 \leq value \leq 25. \\
\end{aligned}
\right.
\]
代码
dp数组版
class Solution {
public int translateNum(int num) {
// dp[i] = dp[i - 1] + dp[i - 2]
String string = String.valueOf(num);
if(string.length() == 1){
return 1;
}
int[] dp = new int[string.length()];
dp[0] = 1;
dp[1] = Integer.valueOf(string.substring(0, 2)) > 25 ? 1 : 2;
for(int i = 2; i < string.length(); i++){
String subString = string.substring(i - 1, i + 1);
int value = Integer.valueOf(subString);
if(10 <= value && value <= 25){
dp[i] = dp[i - 1] + dp[i - 2];
}else{
dp[i] = dp[i - 1];
}
}
return dp[string.length() - 1];
}
}
空间优化版
class Solution {
public int translateNum(int num) {
// dp[i] = dp[i - 1] + dp[i - 2]
String string = String.valueOf(num);
if(string.length() == 1){
return 1;
}
int pre = 1;
int pre2 = Integer.valueOf(string.substring(0, 2)) > 25 ? 1 : 2;
int next = pre2;
for(int i = 2; i < string.length(); i++){
String subString = string.substring(i - 1, i + 1);
int value = Integer.valueOf(subString);
if(10 <= value && value <= 25){
next = pre + pre2;
}else{
next = pre2;
}
pre = pre2;
pre2 = next;
}
return next;
}
}