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

news2025/1/31 3:02:59

🎈算法那些事专栏说明:这是一个记录刷题日常的专栏,每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新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",也会解码失败。

我们可以s[i]和s[i-1]是否为0做分类分析:
若s[i]==‘0’,则我们再判断s[i-1]是否为’0’,如果s[i]==0&&s[i-1]==0,则出现连续两个0,这种排列方式无法解码,直接返回0;如果s[i]==0&&s[i-1]!=0,即s[i]无法单独解码,需要与s[i-1]组合在一起才能解码,如果s[i]与s[i-1]组合数<=26,则dp[i]=dp[i-2],否则return 0(即两个字符无法分开解码,也无法合在一起解码)。
ps:为什么这里dp[i]=dp[i-2]呢?dp[i-2]中保存的是从0号字符到i-2号字符的解码方法。因为上述情况只能组合解码,因此s[i]与s[i-1]需组合解码到一起,所以dp[i]的解码方法与dp[i-2]的解码方法相同。

若s[i]!=‘0’,则我们再判断s[i-1]是否为’0’,如果s[i]!=0&&s[i-1]==0,则两个字符无法一起解码,需要s[i-1]与s[i-2]各自解码,因此dp[i]=dp[i-1](此时出现前导0的情况,前导0无法和s[i]组合,但可以与s[i-2]组合;如果无法与s[i-2]组合,则上一次迭代已经return 0了);如果s[i]!=‘0’&&s[i-1]!=‘0’,若两个数字组合数<=26,则dp[i]=dp[i-1]+dp[i-2],若组合数>26,则dp[i]=dp[i-1]。
ps:这里怎么有dp[i]=dp[i-1]+dp[i-2]?因为s[i]能和s[i-1]组合解码,其组合解码后,s[0-i]的解码方法数与dp[i-2]相同;s[i]和s[i-1]分开解码,则s[0-i]阶解码方法数与dp[i-1]相同,故得到上述式子。
在这里插入图片描述
经过上面的分析,我们可以得到如下代码↓↓↓

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];
    }
};

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

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

本文存在不足,欢迎留言或私信批评、指正。希望我的解决方法能够对你有所帮助~~
今日打卡完成,点亮小星星☆→★

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1279055.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机网络笔记】802.11无线局域网

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

【海思SS528 | VO】MPP媒体处理软件V5.0 | VO模块编程总结

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

<DB2>《AIX服务器DB2数据库裸设备表空间扩容方案》

《AIX服务器DB2数据库裸设备表空间扩容方案》 1 表空间状态查看1.1 连接数据库&#xff08;实例用户操作&#xff09;1.2 查看表空间剩余&#xff08;实例用户操作&#xff09;1.3 查看所属表空间的容器属性&#xff08;实例用户操作&#xff09; 2 VG状态和LV状态查看2.1 查看…

Python 爬虫 之scrapy 框架

文章目录 常用的命令开始爬虫请求与响应让控制台只输出想要的信息创建一个py 文件来帮忙运行爬虫 工作原理图实战 常用的命令 Scrapy是一个用于爬取网站数据的Python框架&#xff0c;以下是一些常用的Scrapy命令&#xff1a; 开始的时候 用 cd 进入你想创建scrapy 的文件夹 &a…

EasyExcel写入多个sheet

直接上代码&#xff1a; public static void main(String[] args) {// 设置excel工作簿ExcelWriter excelWriter EasyExcel.write("F:\\excel\\a.xls").build();List<User> userList new ArrayList<>();userList.add(new User("lisi", "…

QT 中使用 QTableView 和 QStandardItemModel 实现将数据导出到Excel 和 从Excel导入到 QTableView 的功能

简介 在Qt中&#xff0c;使用QTableView和QStandardItemModel来实现将数据导出到Excel和从Excel导入到QTableView的功能&#xff0c;而不使用第三方库&#xff08;如QXlsx&#xff09;。 效果 将 QTableView 中的数据导出到Excel //从tableview 导出到 EXcle void MainInterfa…

数据结构第7次练习-图(基础篇)

一&#xff1a;判断题 1-1 答案&#xff1a;T 解析&#xff1a;c到a的最短路径是12214&#xff0c;所以是大于10的 1-2 答案&#xff1a;T 一个连通分量要进行一次广度优先搜索 1-3 答案&#xff1a;F 解析&#xff1a;是存在等于顶点的个数减一的情况&#xff0c;比如三个顶点…

六、ZooKeeper Java API操作

目录 1、引入maven坐标 2、节点的操作 这里操作Zookeeper的JavaAPI使用的是一套zookeeper客户端框架 Curator ,解决了很多Zookeeper客户端非常底层的细节开发工作 。 Curator包含了几个包:

【大数据】HBase 中的列和列族

&#x1f60a; 如果您觉得这篇文章有用 ✔️ 的话&#xff0c;请给博主一个一键三连 &#x1f680;&#x1f680;&#x1f680; 吧 &#xff08;点赞 &#x1f9e1;、关注 &#x1f49b;、收藏 &#x1f49a;&#xff09;&#xff01;&#xff01;&#xff01;您的支持 &#x…

GODOC命令无效,原因是需要手动安装

在看《GO程序设计语言》这本书&#xff0c;按照其中的内容&#xff0c;想看下GO自带的包的文档。 书中讲&#xff0c;可以直接输入GoDOC命令来打开一个服务器&#xff0c;从而可以用浏览器访问文档库。输入命令后&#xff0c;系统提示找不到该命令。 查了资料后才发现&#xff…

如何使用vue组件

目录 1&#xff1a;组件之间的父子关系 2&#xff1a;使用组件的三个步骤 3&#xff1a;components组件的是私有子组件 4&#xff1a;在main.js文件中使用Vue.component全局注册组件 1&#xff1a;组件之间的父子关系 一&#xff1a;首先封装好的组件是不存在任何的关系的…

国际语音群呼系统的产品优势有哪些?为什么要使用国际语音群呼系统?

一、国际语音群呼系统的产品优势&#xff1a; 1.巨量群呼 支持大容量并发群呼&#xff0c;呼叫不受限制&#xff0c;充裕的线路保障造就百万级平台容量&#xff0c;可以短时间内同时拨打大量电话&#xff0c;让语音快速到达&#xff0c;大大提高发送效率&#xff1b; 2.自主…

【重点】【哈希】128.最长连续序列

题目 思路&#xff1a;https://leetcode.cn/problems/longest-consecutive-sequence/solutions/2362995/javapython3cha-xi-biao-ding-wei-mei-ge-l-xk4c/?envTypestudy-plan-v2&envIdtop-100-liked class Solution {public int longestConsecutive(int[] nums) {Set<…

Spring之RestTemplate详解

Spring之RestTemplate详解 1 RestTemplate1.1 引言1.2 环境配置1.2.1 非Spring环境下使用RestTemplate1.2.2 Spring环境下使用 RestTemplate1.2.3 Spring环境下增加线程号 1.3 API 实践1.3.1 GET请求1.3.1.1 不带参请求1.3.1.2 带参的get请求(使用占位符号传参)1.3.1.3 带参的g…

模式识别与机器学习(七):集成学习

集成学习 1.概念1.1 类型1.2 集成策略1.3 优势 2. 代码实例2.1boosting2.2 bagging2.3 集成 1.概念 集成学习是一种机器学习方法&#xff0c;旨在通过组合多个个体学习器的预测结果来提高整体的预测性能。它通过将多个弱学习器&#xff08;个体学习器&#xff09;组合成一个强学…

[蓝桥杯 2019 省 B] 特别数的和-C语言的解法

小明对数位中含有 2、0、1、9 的数字很感兴趣&#xff08;不包括前导 0&#xff09;&#xff0c;在 1 到 40 中这样的数包括 1、2、9、10 至 32、39 和 40&#xff0c;共 28 个&#xff0c;他们的和是 574。 请问&#xff0c;在 1 到 n 中&#xff0c;所有这样的数的和是多少&…

如何创建一个vue工程

1.打开vue安装网址&#xff1a;安装 | Vue CLI (vuejs.org) 2.创建一个项目文件夹 3.复制地址 4.打开cmd&#xff0c;进入这个地址 5.复制粘贴vue网页的安装命令 npm install -g vue/cli 6.创建vue工程 vue create vue这里可以通过上下键来进行选择。选最后一个选项按回车。 …

【MySQL】-日志系统

一、背景介绍 MySQL中提供了各种各样的日志&#xff0c;每一个日志在不同的阶段有不同的作用&#xff0c;对数据的一致性和正确性得到保障&#xff0c;为数据恢复也提供至关重要的作用&#xff0c;那今天我们一起来讨论讨论MySQL中的各个日志 二、正文 binlog&#xff1a;…

Linux C/C++ 分析网络流量(十六进制TCP数据包分析)

在分析TCP数据包时&#xff0c;理解TCP协议的工作原理和报文格式是关键。TCP是一种面向连接的、提供可靠的、端到端的字节流传输服务。其头部结构包括源端口、目标端口、序列号、确认应答号等字段。序列号是在建立连接时由计算机生成的随机数作为初始值&#xff0c;每发送一次数…