力扣 91.解码方法

难度 中等

原题

91. 解码方法 - 力扣(LeetCode)

一条包含字母 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];
        }

  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值