题目
一条包含字母 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 = “06”
输出:0
解释:“06” 无法映射到 “F” ,因为存在前导零(“6” 和 “06” 并不等价)。
提示:
1 <= s.length <= 100
s 只包含数字,并且可能包含前导零。
思路
- 判断字符串最左边第一个位置、第一第二个位置在一起这两种情况是否可以解码,之后的解码方式依赖之前的解码方式
- 动态规划思想解题,新建dp[ ]表,dp[i]表示以i位置为结尾时,解码方法的总数
- 根据最近的一步来划分问题。将字符串转换为字符数组c[ ],看c[i]是否可以单独解码;c[i - 1]和c[i]在一起是否可以解码。不用考虑i+1的情况
- 解码成功与失败
- c[i]单独解码成功时,c[i]需要满足’1’ <= c[i] <= ‘9’ ,也即 c[i] != ‘0’
- c[i - 1]和c[i]在一起解码成功时,不允许出现’06’类似情况;则c[i]需要满足c[i - 1] != ‘0’,且组合的字符m必须在可解码的范围内,则有 ‘10’ <= m <= ‘26’
- 解码方案数:
- c[i]可以单独解码时,解码方案就相当于在索引为i - 1的字符处的所有方案拼接上c[i] 的解码,方案的数量相当于等于索引为i - 1的字符的方案数;在dp[ ]中的数值就等于dp[i - 1]
- c[i - 1]和c[i]可以在一起解码时,解码方案就相当于在索引为i - 2的字符处的所有方案拼接上 c[i - 1]和c[i] 的解码,方案的数量相当于等于索引为i - 2的字符的方案数;在dp[ ]中的数值就等于dp[i - 2]
- 总的解码方案数就等于以上两种情况解码成功时的和
- 返回值为dp[c.length - 1]
要点
- 边界值,当字符串s长度为1时,需要增加判断语句
- 判断c[i]中的值的时候,由于c[ ]为字符数组,数据类型是char,不能直接与int类型作比较,比较时,需要统一数据类型
- 新创建的dp[]表的所有位置默认为0
代码
public int numDecodings(String s) {
char[] c = s.toCharArray();
int[] dp = new int[c.length];
if(c[0] != '0') dp[0] = 1;
if(c.length == 1) return dp[0];
if(c[0] != '0' && c[1] != '0') dp[1] += 1;
int n = (c[0] - '0') * 10 + c[1] - '0';
if(n >= 10 && n <= 26) dp[1] += 1;
for(int i = 2;i < c.length;i ++){
if(c[i] != '0') dp[i] += dp[i - 1];
int m = (c[i - 1] - '0') * 10 + c[i] - '0';
if(m >= 10 && m <= 26) dp[i] += dp[i - 2];
}
return dp[c.length - 1];
}
边界优化
通过观察上面的代码,可以发现在初始化dp[1]的时候,与填表的代码相似度很大。此时通常采用在dp[]表中新增一个虚拟位置dp[0]来优化初始化的代码
- 新增的dp[0] 的值是多少?
新增节点dp[0]一般情况下值为0。但是本题中,dp[0]影响的是dp[2]的值;当dp[0]和dp[1]结合可以解码成功时,dp[2]的值应该等于dp[1] + dp[0],dp[1]为s的第一个字符,与之前初始化时的值相同;dp[0]的值如果为0,那么就忽略了结合解码成功这种方式,所以dp[0] = 1才能保证后续的值保持不变。
例如s=“91”,结合解码失败时,if语句 if(m >= 10 && m <= 26) dp[i] += dp[i - 2]不成立,dp[2]的值仍为上面if(c[i] != ‘0’) dp[i] += dp[i - 1];该行代码的值,即可以单独解码时的值 - 索引对应关系如何变化(映射关系)?
由于新增一个虚拟节点dp[0];则则dp[i] 的值是看c[i - 1]位置字符是否可以解码成功;
例如dp[1] 的值是看c[1 - 1]位置字符是否可以解码成功;
优化后代码
public int numDecodings_1(String s) {
char[] c = s.toCharArray();
int[] dp = new int[c.length + 1];
dp[0] = 1;
if(c[1 - 1] != '0') dp[1] = 1;
if(c.length == 1) return dp[1];
for(int i = 2;i < c.length;i ++){
if(c[i - 1] != '0') dp[i] += dp[i - 1];
int m = (c[i - 2] - '0') * 10 + c[i - 1] - '0';
if(m >= 10 && m <= 26) dp[i] += dp[i - 2];
}
return dp[c.length];
}
```