「前端刷题」91. 解码方法

206 阅读3分钟

「这是我参与11月更文挑战的第29天,活动详情查看:2021最后一次更文挑战」。

题目

链接:leetcode-cn.com/problems/de… 一条包含字母 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 = "0" 输出: 0 解释: 没有字符映射到以 0 开头的数字。 含有 0 的有效映射是 'J' -> "10" 和 'T'-> "20" 。 由于没有字符,因此没有有效的方法对此进行解码,因为所有数字都需要映射。

示例 4:

输入: s = "06" 输出: 0 解释: "06" 不能映射到 "F" ,因为字符串含有前导 0("6""06" 在映射中并不等价)。

提示:

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

解题思路

思路1,动态规划

  • 思路

    'A' -> 1
    'B' -> 2
    ...
    'Z' -> 26

    • 由示例和题中转码公式可知
      • 数字转换为字母,最大支持26,最小支持1:
        • 数字组合最大为两位数,且小于27,大于0
          • 当为1位数时,形如X
            • 0
              • 首字母,返回0
              • 非首字母,跳过,此处组合为0
            • 非0
              • 本身为1个组合
            • 转移方程
              • s[i-1] != 0 && dp[i] = dp[i-1] + dp[i]
          • 当为2位数时,形如nX
            • 1X
              • 10、11、12...19 所以X可以是任意值
            • 转移方程
              • s[i-2] == 1 && dp[i] = dp[i] + dp[i-2]
            • 2X
              • 20、21、21...26 所以 X < 7
            • 转移方程
              • s[i-2] == 2 && s[i-1] < 7 && dp[i] = dp[i] + dp[i-2]
            • 3X、4X、5X、6X、7X、8X、9X
              • 30、40、50、60、70、80、90
              • 所以 n > 3 时,X只能为0,可以直接跳过
              • 因此可以归纳到第一种情况中去,为1位数:形如X,直接跳过
        • 数字为 0 时,无法解码
          • 又根据题意,首数字必须可以解码,如果数字第一位为0,则解码总数为0,视为无效
/**
 * @param {string} s
 * @return {number}
 */
var numDecodings = function(s) {
    if(s[0] == 0){
        return 0;
    }
    let n = s.length;
    let dp = new Array(n+1).fill(0);
    dp[0] = dp[1] = 1;
    for(let i = 2;i <= n;i++){
        if(s[i-1] != 0){
            dp[i] += dp[i-1];
        }
        if((s[i-2] == 1) || (s[i-2] == 2 && s[i-1] <= 6)){
            dp[i] += dp[i-2];
        }
    }
    return dp[n];
};

思路2,递归

  • 通过索引 穷举 i-2和i-1的所有可能组合情况
/**
 * @param {string} s
 * @return {number}
 */
var numDecodings = function(s) {
    if(s[0] == 0){
        return 0;
    }
    let n = s.length;
    let helper = (start) => {
        if(start == n){
            return 1;
        }
        if(s[start] == 0){
            return 0;
        }
        let odd = helper(start+1);
        let even = 0;
        if(start < n - 1){
            let ten = s[start];
            let one = s[start+1];
            if((ten+''+one) < 27){
                even = helper(start+2);
            }
        }
        return odd + even;
    }
    return helper(0);
};

思路3,递归 + 备忘录

/**
 * @param {string} s
 * @return {number}
 */
var numDecodings = function(s) {
    if(s[0] == 0){
        return 0;
    }
    let n = s.length;
    let memo = new Map();
    let helper = (start) => {
        if(start == n){
            return 1;
        }
        if(s[start] == 0){
            return 0;
        }
        let memoVal = memo.get(start);
        if(memoVal){
            return memoVal;
        }
        let odd = helper(start+1,memo);
        let even = 0;
        if(start < n - 1){
            let ten = s[start];
            let one = s[start+1];
            if((ten+''+one) < 27){
                even = helper(start+2,memo);
            }
        }
        let res = odd + even;
        memo.set(start,res);
        return res;
    }
    return helper(0,memo);
};