【动态规划】LeetCode-91.解码方法

🎈算法那些事专栏说明:这是一个记录刷题日常的专栏,每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目,在这立下Flag🚩

🏠个人主页:Jammingpro

📕专栏链接:算法那些事

🎯每日学习一点点,技术累计看得见

题目

题目描述

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

题解

首先,我们通过上面的解码规则,对"123"进行解码。"1"、"2"、"3"均能够单独解码,这是一种解码方法;将"1"和"2"组合解码为"12",其范围在1到26之间,故解码成功,因此"12"、"3"是一种解码方法;将"1"单独解码,"2"和"3"组合解码为"23",其范围在1到26之间,故也解码成功,因此"1"、"23"也是一种解码方法。所以"123"共有3种解码方法。

再看一个示例:对"1032"进行解码。"1"、"0"、"3"、"2"无法分开单独解码,因为"0"不在1到26范围内,因此,"0"需要与"1"或"3"组合解码。"1"和"0"组合解码为"10",在1到26之间;"0"和"3"组合解码为"03",因为其包含前导0,故不满足题目要求。因此,"0"必须与"1"组合解码。"3"和"2"可以分开解码,但无法组合解码,因为其组合为"32",超出26,故解码失败。这个字符串只有一种解码方法,即1032

由上面分析可知,遇到①组合数有前导0、②构成数不在1到26之间,将会导致解码失败。除此之外,出现连续两个0,即"00",也会解码失败。

我们可以si和si-1是否为0做分类分析:

若si=='0',则我们再判断si-1是否为'0',如果si==0&&si-1==0,则出现连续两个0,这种排列方式无法解码,直接返回0;如果si==0&&si-1!=0,即si无法单独解码,需要与si-1组合在一起才能解码,如果si与si-1组合数<=26,则dpi=dpi-2,否则return 0(即两个字符无法分开解码,也无法合在一起解码)。

ps:为什么这里dpi=dpi-2呢?dpi-2中保存的是从0号字符到i-2号字符的解码方法。因为上述情况只能组合解码,因此si与si-1需组合解码到一起,所以dpi的解码方法与dpi-2的解码方法相同。

若si!='0',则我们再判断si-1是否为'0',如果si!=0&&si-1==0,则两个字符无法一起解码,需要si-1与si-2各自解码,因此dpi=dpi-1(此时出现前导0的情况,前导0无法和si组合,但可以与si-2组合;如果无法与si-2组合,则上一次迭代已经return 0了);如果si!='0'&&si-1!='0',若两个数字组合数<=26,则dpi=dpi-1+dpi-2,若组合数>26,则dpi=dpi-1

ps:这里怎么有dpi=dpi-1+dpi-2?因为si能和si-1组合解码,其组合解码后,s0-i的解码方法数与dpi-2相同;si和si-1分开解码,则s0-i阶解码方法数与dpi-1相同,故得到上述式子。

经过上面的分析,我们可以得到如下代码↓↓↓

cpp 复制代码
class Solution {
public:
    int numDecodings(string s) {
        if(s[0] == '0') return 0;
        int n = s.size();
        if(n == 1) return 1;
        vector<int>dp(n);
        dp[0] = 1;
        if(s[1] == '0')
        {
            if((s[0] - '0') * 10 + (s[1] - '0') <= 26)
                dp[1] = 1;
            else
                return 0;
        }
        else
        {
            if((s[0] - '0') * 10 + (s[1] - '0') <= 26)
                dp[1] = 2;
            else
                dp[1] = 1;
        }

        for(int i = 2; i < n; i++)
        {
            if(s[i] == '0' && s[i - 1] == '0') return 0;
            if(s[i] == '0')
            {
                if((s[i - 1] - '0') * 10 + (s[i] - '0') <= 26)
                {
                    if(s[i - 1] == '0')
                        dp[i] = dp[i - 1];
                    else
                        dp[i] = dp[i - 2];
                }
                else
                    return 0;
            }
            else
            {
                if((s[i - 1] - '0') * 10 + (s[i] - '0') <= 26)
                {
                    if(s[i - 1] == '0')
                        dp[i] = dp[i - 1];
                    else
                        dp[i] = dp[i - 1] + dp[i - 2];
                }
                else
                    dp[i] = dp[i - 1];
            }
        }
        return dp[n - 1];
    }
};

这个代码看起来非常繁琐,嵌套了需要分支语句,可阅读性较差。下面我们对这个代码做一些优化↓↓↓

cpp 复制代码
class Solution {
public:
    int numDecodings(string s) {
        int n = s.size();
        vector<int>dp(n + 1);
        dp[0] = 1;//这里没有实质性含义,因为s[0]与s[1]组合数若满足10-26,需要+dp[0],故初始化为1
        dp[1] = s[0] != '0';
        for(int i = 2; i <= n; i++)
        {
            int t = (s[i - 2] - '0') * 10 + (s[i - 1] - '0');//计算组合数
            dp[i] = s[i - 1] != '0' ? dp[i - 1] : 0;//当前字符不为0,则可以单独解码,则dp[i]=dp[i-1]
            dp[i] += t >=10 && t <= 26 ? dp[i - 2] : 0;//组合数在10-26之内,则+dp[i-2]
        }
        return dp[n];
    }
};

优化代码中的dp下标与s下标差1,代码思路与前一代码类似,但更为巧妙。

本文存在不足,欢迎留言或私信批评、指正。希望我的解决方法能够对你有所帮助~~

今日打卡完成,点亮小星星☆→★

相关推荐
江畔柳前堤8 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore9 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考10 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX11 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面12 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号12 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽12 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI12 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者14 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者14 小时前
【征程6】校准量化中HistogramObserver解析
算法