【JS每日一算法】🟨85.解码方法(递归回溯、动态规划)

484 阅读1分钟

一条包含字母 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.length <= 100
  • s 只包含数字,并且可能包含前导零。

示例 1:

输入:s = "12"
输出:2
解释:它可以解码为 "AB"1 2)或者 "L"12)。

示例 2:

输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。

题解:

更多JS版本题解点击链接关注该仓库👀

/**
 * @description: 递归回溯   TC:O(2^n)  SC:O(1)
 * @author: JunLiangWang
 * @param {*} s 给定字符串S
 * @return {*}
 */
function recursionBackTracking(s){
    /***
     * 该方案使用递归回溯的方式,逐个以1个/2个字符
     * 组成解码方法递归字符串S。
     */

    // 记录解码方式数量
    let count=0;

    /**
     * @description: 递归
     * @author: JunLiangWang
     * @param {*} index 当前递归到字符串S索引位置
     * @return {*}
     */    
    function cursion(index){
        // 如果索引超出s长度,证明已遍历完字符串所
        // 有字符满足解码要求,此时解码方式+1
        if(index>=s.length){
            count++;
            return ;
        }
        // 如果当前字符等于0,无论是以该字符单独解码
        // 还是以它作为首位,与其他字符组合解码都是
        // 无法解码的,例如:'0'或'02'。此时结束递归
        if(s[index]!=0){
          // 以该字符单独解码的方式,继续递归。
          cursion(index+1)
          // 如果该字符满足与下一个字符组合解码
          if(s[index+1]!=undefined&&s[index]*10+s[index+1]*1<=26)
          // 则与下一个字符组合解码,继续递归。
          cursion(index+2)
        }
    }
    // 执行递归
    cursion(0)
    // 返回结果
    return count;
}


/**
 * @description: 动态规划  TC:O(n)  SC:O(n)
 * @author: JunLiangWang
 * @param {*} s  给定字符串s
 * @return {*}
 */
function DP(s){
    /**
     * 该方案使用动态规划的方式,试想字符串为以下:
     *  
     *   s =   '1'      n =    1
     *         '11'            2
     *         '111'           3
     *         '1111'          5
     *         '11111'         8
     * 
     * 大家是否看出了规律,当前字符串的解码的解码方式
     * 等于上两个解码方式之和,但这并没有涵盖字符串S的
     * 所有情况,例如下一个:
     * 
     *   s =   '1'      n =    1
     *         '11'            2
     *         '110'           1
     *         '1101'          1
     *         '11011'         2
     * 
     * 当遇到0时,该字符串的解码方式不再等于上两个之和了
     * 而是等于前两个的解码方式,这是由于0必须和前一个字符
     * 组合,因此他们便成为了一个整体,其解码方式等同于删除
     * 它们两个后的解码方式,这也并未涵盖字符串s的所有情况
     * 例如下一个:
     * 
     *   s =   '1'      n =    1
     *         '13'            2
     *         '132'           2
     *         '1321'          4
     *         '13211'         6
     * 当遇到字符两者组合大于26时,此时无法组合成有效的解码,
     * 因此当前字符就只能作为单独字符解码,其解码方式就等同于
     * 删除它本身
     * 
     * 以上则涵盖了所有情况,我们可以使用动态规划的方式,记录
     * 上一/两字符解码方式情况,再根据当前字符比较计算得出当前
     * 字符的解码方式。
     */

    // 如果首个字符为0,则该字符串无法解码,直接返回0
    if(s[0]==='0')return 0
    // 初始化动态规划数组,长度加一是由于第二个字符并没有
    // 上两个字符,因此通过加一长度来模拟
    let DPArray=new Array(s.length+1)
    // 初始化第0个字符的解码方式为1
    DPArray[0]=1;
    // 初始化第1个字符的解码方式为1
    DPArray[1]=1
    // 从第二个字符开始遍历字符串S
    for(let i=1;i<s.length;i++){
        // 获取上一个字符的情况
        let lastNum=s[i-1]*1
        // 如果当前字符为0
        if(s[i]==='0')
        {
            // 由于0必须和前一个字符组合,
            // 因此他们便成为了一个整体,
            // 如果两字符组合大于26时,
            // 则无法组合成有效的解码,
            // 直接返回0
            if(lastNum>0&&lastNum<3)
            // 否则其解码方式等同于删除
            // 它们两个后的解码方式
            DPArray[i+1]=DPArray[i-1]
            else return 0
        }
        // 如果当前字符不为0
        else
        {
            // 当两字符组合大于26或上一个字符等于0,
            // 此时无法组合成有效的解码,因此当前字
            // 符就只能作为单独字符解码,其解码数量
            // 等于删除它本身后的解码数量
            if(lastNum==0||lastNum*10+s[i]*1>26)
            DPArray[i+1]=DPArray[i]
            // 否则,字符串的解码的解码方式
            // 等于上两个解码方式之和
            else
            DPArray[i+1]=DPArray[i]+DPArray[i-1]
        }
    }
    // 返回结果
    return DPArray[s.length]
}

来源:力扣(LeetCode)

🚀广告

最近开了一个新专栏《HTTP完全注解》,准备全面介绍下HTTP/0.9到HTTP/2.0,但数据有些惨淡😭,有点失落。如果你想要全面学习了解HTTP,不妨看看,绝对会给你惊喜!