解码方法--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” 并不等价)。

提示:
1 <= s.length <= 100
s 只包含数字,并且可能包含前导零。

思路

  1. 判断字符串最左边第一个位置、第一第二个位置在一起这两种情况是否可以解码,之后的解码方式依赖之前的解码方式
  2. 动态规划思想解题,新建dp[ ]表,dp[i]表示以i位置为结尾时,解码方法的总数
  3. 根据最近的一步来划分问题。将字符串转换为字符数组c[ ],看c[i]是否可以单独解码;c[i - 1]和c[i]在一起是否可以解码。不用考虑i+1的情况
  4. 解码成功与失败
    • 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’
  5. 解码方案数:
    • 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]
    • 总的解码方案数就等于以上两种情况解码成功时的和
  6. 返回值为dp[c.length - 1]

要点

  1. 边界值,当字符串s长度为1时,需要增加判断语句
  2. 判断c[i]中的值的时候,由于c[ ]为字符数组,数据类型是char,不能直接与int类型作比较,比较时,需要统一数据类型
  3. 新创建的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]来优化初始化的代码

  1. 新增的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];该行代码的值,即可以单独解码时的值
  2. 索引对应关系如何变化(映射关系)?
    由于新增一个虚拟节点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];
    }
    ```
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值