作者:Grey
原文地址:
一条包含字母 A-Z 的消息通过以下映射进行了 编码 :
'A' -> 1
'B' -> 2
...
'Z' -> 26
要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,"11106" 可以映射为:
"AAJF" ,将消息分组为 (1 1 10 6)
"KJF" ,将消息分组为 (11 10 6)
注意,消息不能分组为 (1 11 06) ,因为 "06" 不能映射为 "F" ,这是由于 "6" 和 "06" 在映射中并不等价。
给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数 。
题目数据保证答案肯定是一个 32 位 的整数。
示例 1:
输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。
示例 2:
输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
示例 3:
输入:s = "0"
输出:0
解释:没有字符映射到以 0 开头的数字。
含有 0 的有效映射是 'J' -> "10" 和 'T'-> "20" 。
由于没有字符,因此没有有效的方法对此进行解码,因为所有数字都需要映射。
示例 4:
输入:s = "06"
输出:0
解释:"06" 不能映射到 "F" ,因为字符串含有前导 0("6" 和 "06" 在映射中并不等价)。
定义递归函数int process(int i, char[] str)
递归含义表示:从 i 一直到最后,得到的解码方法数有多少
base case 是:
当 i 已经大于 str.length,说明之前的解码决策有问题,直接返回 0。
当 i 正好等于 str.length, 说明之前的决策正好有一种符合条件的情况,返回 1。
接下来就是普遍情况,即:i 小于 str.length, 此时,有如下几种决策情况
第一种情况
str[i]=='0'
,由于 0 无法解码成任何字符,也无法和后一个进行拼凑成一个字符的编码,所以,直接返回 0。表示决策无效。
第二种情况
str[i] == '1'
, 则可以有如下决策,首先,str[i]
位置独立编码成一个字符,或者str[i]
和str[i+1]
结合解码成一个字符。
第三种情况
str[i] == '2'
, 则可以有如下决策,首先,str[i]
位置独立编码成一个字符,或者str[i]
和str[i+1]
结合解码成一个字符,但是此时的str[i+1]
的字符有条件,即:
i + 1 < str.length && str[i + 1] <= '6'
只有满足这个条件,str[i]
才能和str[i+1]
结合解码成一个字符。
第四种情况
str[i] > '2'
, 则str[i]
只能单独解码成一个字符。
暴力解法的完整代码如下
class Solution {
public static int numDecodings(String s) {
if (null == s || s.length() < 1) {
return 0;
}
char[] str = s.toCharArray();
return process(0, str);
}
// 从i一直到最后,得到的解码数
public static int process(int i, char[] str) {
if (i > str.length) {
return 0;
}
if (i == str.length) {
return 1;
}
// i < str.length
if (str[i] == '0') {
return 0;
}
if (str[i] == '1') {
int p1 = process(i + 1, str);
int p2 = process(i + 2, str);
return p1 + p2;
}
if (str[i] == '2') {
int p1 = process(i + 1, str);
if (i + 1 < str.length && str[i + 1] <= '6') {
p1 += process(i + 2, str);
}
return p1;
}
return process(i + 1, str);
}
}
直接超时
有了上述暴力递归解法,可以直接改成动态规划解法,由于递归函数只有一个可变参数,所以定义一个一维数组即可装下所有可能性。
int[] dp = new int[str.length + 1];
dp[i]
的含义和递归函数process(i,str)
的含义一样,都是从 i 开始到最后,解码数量是多少。
由于暴力递归方法中,process(i,str)
依赖process(i+1,str)
和process(i+2,str)
所以对于 dp 数组来说, dp[i]
的值依赖dp[i+1]
和dp[i+2]
决策的结果,
根据暴力递归方法中的 base case,可以得到 dp 的某些行列的初始值,然后根据递推公式进行递归,最后返回dp[0]
就是结果。
动态规划解的完整代码如下
class Solution {
public static int numDecodings(String s) {
if (null == s || s.length() < 1) {
return 0;
}
char[] str = s.toCharArray();
int[] dp = new int[str.length + 1];
dp[str.length] = 1;
for (int i = str.length - 1; i >= 0; i--) {
if (str[i] == '0') {
dp[i] = 0;
} else {
dp[i] = dp[i + 1];
if (str[i] == '1' && i + 1 < str.length) {
dp[i] = dp[i] + dp[i + 2];
} else if (str[i] == '2' && i + 1 < str.length && str[i + 1] <= '6') {
dp[i] += dp[i + 2];
}
}
}
return dp[0];
}
}