「这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战」
前言
力扣第九十一题 解码方法 如下所示:
一条包含字母 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) 。
一、思路
题目中有两个比较重要的信息:
- 只需要保留最后解码 方法的 总数
- 字符串中仅含有数字,可能会有前导
0 - 类似于
06是不可以被转换的
对于仅含有数字的字符,这省去了我们对于其他字符的判断。
我们发现一位数字 1 ~ 9 都是可以被转换的,两位数字的话需要判断是否在 10 ~ 26 这个区间。至于 0 的话就需要与它前面的那一个数字合在一起看区间是否在 10 ~ 26。
我们发现 i (0 < i < s.len) 处编码的数量与 i-1 处编码的数量是有关系的。如下图所示:
假设我们直到 i-1 处的编码结果为 n,那么 m 处的编码结果呢?
很显然此时我们需要分情况讨论了,如下所示:
s[i] != 0且s[i-1] + s[i] <= 26:结果为ret[i-1] + ret[i-2](还需要加上i-2处的结果)s[i] != 0且s[i-1] + s[i] > 26时:结果为ret[i-1]s[i] == 0且s[i-1] + s[i] <= 26时:结果为ret[i-2]- 其他情况,均为
0
根据上面的分析,我们很容易想到这一题可以使用动态规划来解决,大致的步骤如下所示:
因为使用的动态规划,所以存储结果的地方使用的一维数组
dp[]
- 遍历整个字符串
- 判断是否为
0,并根据规则填充dp[i] - 遍历结束,返回
dp[len-1]的结果即可
二、实现
实现代码
实现代码与思路中保持一致,唯一值得注意的地方就是:需要考虑一些边界的情况。
当 s[i-1] + s[i] 是可以被解码时,但此时 i-2 < 0,即前面没有元素了,我们只需要加上 1 即可。(自身也算一种结果)
public int numDecodings(String s) {
// 特殊情况
if (s.charAt(0) == '0')
return 0;
int len = s.length();
int[] dp = new int[len];
dp[0] = 1; // 初始化
for (int i = 1; i < len; ++i) {
if (s.charAt(i) != '0') {
dp[i] += dp[i - 1];
}
if (s.charAt(i - 1) != '0' && ((s.charAt(i - 1) - '0') * 10 + (s.charAt(i) - '0') <= 26)) {
dp[i] += i > 1 ? dp[i - 2] : 1; // 如果只有两个字符,则加上本身即可
}
}
return dp[len-1];
}
测试代码
public static void main(String[] args) {
new Number91().numDecodings("2102");
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~