【动态规划】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,代码思路与前一代码类似,但更为巧妙。

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

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

相关推荐
JieE21215 小时前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE21215 小时前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术19 小时前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦20 小时前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
用户497863050731 天前
(一)小红的数组操作
算法·编程语言
怕浪猫1 天前
Electron 系列文章封面图
算法·架构·前端框架
徐小夕1 天前
JitWord 3.0 正式发布,高精度Word异构解析+复杂组件兼容,打造web端协同Word编辑器
前端·vue.js·算法
通信小呆呆2 天前
当算法有了“五感”:多模态数据融合如何向人体感官协同学习?
人工智能·学习·算法·机器学习·机器人