难度 中等
原题
一条包含字母
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" 并不等价)。
解法
首先,写这篇文章的目的是因为自己终于理解题解了,真技术还得看宫水三叶大神的,我就一搬运工。(还好没多少人会在CSDN查力扣题解)
题意非常清楚我就不多说了,这一题还是动态规划,用f[i]表示前i个数字的解码数量,f[i]默认初始化的值为0,如果碰到 1230 这种可以直接返回0
f [0]记作1,后面会用到
从f [1]开始,前一位的数量起决于其是否为0,如果其为0,别说f [1],整个题都直接返回0了,因为题目规定只允许 6 ,不允许 06 ,所以要判断是否为0,当不为0时,前 i 个数字的组合数不会比前 i-1 个数字小,比如
{3,4,5,6}
仅有 3,4,5,6 这一种组合
{3,4,5,6,1}
即使数字量增加了,但是仍然只有3,4,5,6,1这一种组合
{3,4,5,6,1,2}
由于1和2可以组成1,2和12,所以就有
3,4,5,6,1,2
3,4,5,6,12
两种组合。
但这也只是多了一种组合,还是无法获得规律,那就继续增加为
{3,4,5,6,1,2,3}
现在就有
3,4,5,6,1,2,3
3,4,5,6,12,3
3,4,5,6,1,23
这三种组合了,这里我需要再举个例子将最后一个数换为0
{3,4,5,6,1,2,0}
3,4,5,6,1,20
又只剩下一种了
由上可以看出,添加数字1后,添加之前的结尾的6无法和1组成小于等于26的数字,对于原来的组合来说不过是末尾追加了一个数字,没有带来任何新组合,所以此时
f [i]=f [i-1]
由于f [i]默认为0,所以也可以理解为如果这一位是大于0的数字,则获得前 i-1 位的组合数
f [i]+=f [i-1]
而当前 i 项的最后两位可以组合成新的数字时,(并且题目中要求组合成的数字小于等于26)则新增了一种组合,新增的组合数和前 i-2 位相等,也就是
f [i]+=f [i-2]
为什么是f [i-2],首先可以知道,如果前 i-2 位有m种组合,后2位有n种组合,最后的组合数一定是m*n,后两位的组合n每增加一种,总组合数就会增加m种,即f [i-2]
而任意两位的组合最多也只有 (1,2)和(12)两种,而(1,2)就是默认的组合,所以也只是多了一种(12)的组合,如果最后一位是0,最后两位组成只能10或者20才能作为满足题意的解码,(1,0)和(2,0)都是不符合题意的结果
所以这里会经过两部判断
第一步 当前数字是否大于0:
如果大于0则执行
f [i]+=f [i-1]
否则什么都不做
因为一个数字不会带来任何新组合,{3,4,5}和{3,4,5,6}相比只是后者变长了,但也是一种组合
第二步 当前数字是否能和上一个数字组成新的组合
首先如果当前是0,会跳过第一步,如果上一个是1或者2,则能获得一个新的组合,虽然是第i位,但是两个数字仅有一个组合,和一位一样,因此
f [i]+=f [i-2]
如果最后一位和前一位凑出来的数字大于26,则会跳过这一步
如果两步都跳过了就会返回f [i]的初始值0
public int numDecodings(String s) {
int n = s.length();
char[] cs = s.toCharArray();
//不想转换为数组的也可以使用s.charAt()方法
int[] f = new int[n + 1];
f[0] = 1;
//给前0位一个默认值
for (int i = 1; i <= n; i++) {
int a = cs[i-1] - '0';
//char类型加减法是用ascii码值加减法
//这里减去第0位可获得该char在该类型(数字或是字母)值的位置
if (a > 0)
f[i] = f[i - 1];
//如果当前数字不为0,表明当前数字获得前i个数字的组合数
if (i>1&&0 != (cs[i - 2] - '0') && ((cs[i - 2] - '0') * 10 + (cs[i-1] - '0') <= 26))
f[i] += f[i - 2];
//如果当前数字还能和上一个数字组合成新的数字,则再获得前i-2个数字的组合数
}
return f[n];
}