本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是LeetCode专题的第57篇文章,我们一起来看看LeetCode第91题,解码方法(Decode ways)。

这道题官方给定的难度是Medium,点赞2680,反对2845,通过率24.5%。从通过率上来看这道题似乎很难,甚至比很多Hard难度的问题还要难。从点赞和反对的数量来看,这道题应该算是褒贬不一,两边阵营的人数都很多。一般来说题目的评价都是一边倒为主,这种情况很少出现。首先值得一说的是,这道题我个人肯定认为是值得一做的,至于它是否是一道好问题呢?这个问题就留给大家自己回答吧。

首先我们来看下这道题的题意。

题意

在传送消息的时候我们可以把字母映射成数字,比如A映射成1,B映射成2,Z映射成26。我们这里假设用到的字母只有大写的,也就是说我们用26个数字就可以代表A到Z的所有字母。

现在我们得到了一串数字连成的字符串,想要将它还原成原本字母组成的信息,请问它一共有多少种还原的方法?

样例

Input: "12"
Output: 2
Explanation: It could be decoded as "AB" (1 2) or "L" (12).
Input: "226"
Output: 3
Explanation: It could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

题解

我们观察一下样例,比如12可以理解成12这个字符也就是L,也可以理解成1和2两个字符拼接而成。同样226有三种还原的方法,分别是(2, 2, 6), (22, 6), (2, 26)。我们只需要返回所有可能的数量即可。

这道题看起来没有头绪,但是当我们仔细分析一下其中的情况,其实并不复杂。首先对于字符串当中的每一位来说,只有0到9这10种可能性。我们逐一来考虑,首先如果是0,由于我们的A映射的是1,所以0只能和前面一个数字凑成10或者是20,如果前一位不是1或者是2,那么说明这个字符串是非法的,也就是没有办法还原。

如果某一位是1-9当中的数字,它都可以单独成为一个字母。我们再考虑它的前一位,它的前一位只有1和2这两种可能可以构成新的组成。这里要注意,如果它的前一位是2的话,那么当前位必须要小于6,因为英文字母最多只到26。所以这里也需要进行一个特殊判断,除此之外,就没有其他情况了。

这些可能性都列举出来之后,剩下的就简单了,比如我们可以用深搜来搜索所有解的可能性。由于我们已经列举出了所有的情况,所以这段代码并不难写,但是想要一次就写对也不容易。

class Solution:
    def numDecodings(self, s: str) -> int:
        n = len(s)
        ret = 0
        
        def dfs(i):
            nonlocal ret
            
            if i >= n:
                ret += 1
                return 
            
            # 当前位如果是0则说明无解,return
            # 当前位为0说明上一位不是1或者2
            if s[i] == '0':
                return
            
            # 递归单个的情况
            dfs(i+1)
            
            # 判断下一位能否构成组合
            if i+1 < n and (s[i] == '1' or (s[i] == '2' and ord(s[i+1]) <= ord('6'))):
                dfs(i+2) 
                
        dfs(0)
        return ret

搜索算法固然可以解,但是一定会超时。这一点也不难想明白,当我们的字符串长度大了之后,带来的解的可能性是非常多的。最极端的情况下,比如每一位都是1,它都可以单独作为一个字符也可以和下一位组合成11构成新的字符。这样的情况总数是以斐波那契数列递增的,n不需要多大带来的解的数量就是天文数字了。

使用搜索算法我们需要穷举每一种情况,哪怕寻找每一个解只需要的复杂度也是无法抗住的。

所以我们必须要想一些更优的方法,这个方法也不难想,就是动态规划。

我们分析一下会发现每一位数字能够组成的解只和它的前一位有关,和后面的都没有关系。这样的话显然是满足动态规划的无后效性的。也就是说前面的字符组合的情况不会影响后面的解。

我们假设dp[i]存储的是前i个数字构成的解的数量,对于s[i]来说有10种情况,分别是0到9。如果s[i]为0,那么s[i-1]如果是1或者是2的话,只有一种情况,就是0和s[i-1]组成10或者是20。那么dp[i] = dp[i-2]。

如果s[i]不为0,那么s[i]可以选择单独成为一个字符,那么dp[i] = dp[i-1]。当然如果s[i-1]是1或者是2的话,s[i]也可以选择和s[i-1]合起来组成一个字符。那么这样的情况下,dp[i]需要再额外增加dp[i-2],也就是dp[i]构成的答案可能性增加了。

如果把这些状态之间的转移情况都梳理清楚了,那么这个代码肯定不难写的。

class Solution:
    def numDecodings(self, s: str) -> int:
        n = len(s)
        # 为了简化判断,我们把s前面加上0,这样字符串下标从1开始
        s = '0' + s
        dp = [0 for _ in range(n+2)]
        
        dp[0] = 1
        for i in range(1, n+1):
            # 如果当前位0,那么判断前一位是否是1或2
            # 否则一定无解
            if s[i] == '0':
                if i > 1 and s[i-1] in ('1', '2'):
                    dp[i] = dp[i-2]
                else:
                    return 0
                continue
            dp[i] = dp[i-1]
            # 能和前一位构成字符,那么加上dp[i-2]的数量
            if i > 1 and s[i-1] == '1' or s[i-1] == '2' and ord(s[i]) <= ord('6'):
                dp[i] += dp[i-2]
        return dp[n]

总结

从动态规划的角度上来看,这道题并不算困难,说是迎刃而解也不为过。但如果没有想到动态规划,纠结于搜索算法的话那么可能一直都没有办法AC。

我不清楚给差评的是否都是后一种情况,但单纯从题目的质量上来说,这道题的质量是不错的,是一道很不错的联系动态规划的习题,因此建议大家有时间都能体会一下。

今天的文章到这里就结束了,如果喜欢本文的话,请来一波素质三连,给我一点支持吧(关注、转发、点赞)。

- END -

LeetCode 91,点赞和反对五五开,这题是好是坏由你来评判的更多相关文章

  1. leetcode@ [91] Decode Ways (Dynamic Programming)

    https://leetcode.com/problems/decode-ways/ A message containing letters from A-Z is being encoded to ...

  2. Leetcode 91. Decode Ways 解码方法(动态规划,字符串处理)

    Leetcode 91. Decode Ways 解码方法(动态规划,字符串处理) 题目描述 一条报文包含字母A-Z,使用下面的字母-数字映射进行解码 'A' -> 1 'B' -> 2 ...

  3. [转帖]鲁大师Q3季度PC处理器排行:AMD、Intel终于五五开了

    鲁大师Q3季度PC处理器排行:AMD.Intel终于五五开了 https://www.cnbeta.com/articles/tech/902375.htm 近日,鲁大师发布了Q3季度PC处理器排行. ...

  4. 《学习OpenCV》练习题第五章第二题abc

    代码: #include <stdio.h> #include <opencv/highgui.h> #include <opencv/cv.h> #include ...

  5. 《学习OpenCV》练习题第五章第一题ab

    这道题是载入一幅带有有趣纹理的图像并用不同的模板(窗口,核)大小做高斯模糊(高斯平滑),然后比较用5*5大小的窗口平滑图像两次和用11*11大小的窗口平滑图像一次是否接近相同. 先说下我的做法,a部分 ...

  6. 20155326 第五周加分题--mybash的实现

    第五周加分题--mybash的实现 题目要求 1.使用fork,exec,wait实现mybash 2.写出伪代码,产品代码和测试代码 3.发表知识理解,实现过程和问题解决的博客(包含代码托管链接) ...

  7. 第五周加分题--mybash的实现

    第五周加分题--mybash的实现 题目要求 1.使用fork,exec,wait实现mybash 2.写出伪代码,产品代码和测试代码 3.发表知识理解,实现过程和问题解决的博客(包含代码托管链接) ...

  8. 第五周 加分题-mybash的实现

    第五周 加分题-mybash的实现 使用fork,exec,wait实现mybash 产品代码 #include <stdio.h> #include <stdlib.h> # ...

  9. 2017-2018-1 20155320 第五周 加分题-mybash的实现

    2017-2018-1 20155320 第五周 加分题-mybash的实现 使用fork,exec,wait实现mybash 写出伪代码,产品代码和测试代码 发表知识理解,实现过程和问题解决的博客( ...

随机推荐

  1. Trie——解决字符串搜索、异或最值问题

    Trie--解决字符串搜索.异或最值问题 在说到Trie之前,我们设想如下问题: 给我们1e5个由小写字母构成的不重复的字符串,每个字符串长度不超过6,之后是1e5次查询操作,每次给我们一个字符串,要 ...

  2. Spring-Boot 多数据源配置+动态数据源切换+多数据源事物配置实现主从数据库存储分离

    一.基础介绍 多数据源字面意思,比如说二个数据库,甚至不同类型的数据库.在用SpringBoot开发项目时,随着业务量的扩大,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源. ...

  3. .clearfix 清除浮动,@import

    我们知道,在网页的DIV+CSS布局中,很多时候要用到浮动. 既然有浮动,那就有清除浮动. 清除浮动有很多种方式,而在实际项目中,比较常用的是这一种. .clearfix:after { conten ...

  4. Python 简明教程 --- 24,Python 文件读写

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 过去的代码都是未经测试的代码. 目录 无论是哪种编程语言,IO 操作都是非常重要的部分.I 即Inp ...

  5. 设计模式:fly weight模式

    目的:通过共享实例的方式来避免重复的对象被new出来,节约系统资源 别名:享元模式 例子: class Char //共享的类,轻量级 { char c; public: Char(char c) { ...

  6. Go操作腾讯云COS对象存储的简单使用案例

    准备环境 安装Go环境 Golang:用于下载和安装 Go 编译运行环境,请前往 Golang 官网进行下载 安装SDK go get -u github.com/tencentyun/cos-go- ...

  7. jsp课堂笔记5 Java servlet

    servlet的创建 servlet类就是一个包含javax.servlet.http包中的HttpServlet类 部署web.xml文件 在<sevlet>标签中创建 <serv ...

  8. OpenWrt 编译分割

    本文主要参考:http://macbruins.com/2011/05/08/downloading-sources-for-building-openwrt/ OpenWrt系统在buildroot ...

  9. sed 指定行范围匹配

    sed -n '5,10{/pattern/p}' file sed是一个非交互性性文本编辑器,它编辑文件或标准输入 导出的文件拷贝.标准输入可能是来自键盘.文件重定向.字符串或变量,或者是一个管道文 ...

  10. html中input提示文字样式修改

    在很多网站上我们都看到input输入框显示提示文字,让我们一起来看看如果在input输入框中显示提示文字.我们只需要在<input>标签里添加:placeholder="提示文字 ...