把数字翻译成字符串 VS 解码方法

一、【LeetCode 面试题46】把数字翻译成字符串

1. 题目描述

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。

示例 1:

输入: 12258
输出: 5
解释: 122585种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi""mzi"

提示: 0 <= num < 2^31

2. 解题思路

动态规划还没学明白,先想到的是递归,相对于容易理解一点的思路:

  • 首先把数字转换成字符串;
  • (1)然后先判断递归函数 backtrack 的出口:当下标等于数字的长度时说明已经得到了一个结果,将 self.count 加一然后return即可
    (2)接着处理一位数的情况:将当前位置的字符转换成对应的数字,可以使用:a = ord(s[index]) - ord('0'),也可以直接写为:a = int(s[index]),然后不断递归调用该函数,注意index要相应加一;
    (3)一位数处理完了,就可以处理两位数的情况了,获取两位数的方法很简单:b = a*10 + ord(s[index+1]) - ord('0')将index位作为十位,index+1位作为个位拼在一起即可),得到之后要判断该两位数的合法性,即是否大于等于10,并且小于等于25:if b >= 10 and b <= 25:,只有满足该条件,才能进行下一步递归。

3. 代码

class Solution:
    def translateNum(self, num: int) -> int:
        self.count = 0
        s = str(num)
        self.backtrack(s, 0, len(s))
        return self.count

    def backtrack(self, s, index, length):
        if index == length:
            self.count += 1
            return
        a = ord(s[index]) - ord('0')
        self.backtrack(s, index + 1, length)  # 处理一位数的情况
        if index < length - 1:
            b = a*10 + ord(s[index+1]) - ord('0')
            if b >= 10 and b <= 25:
                self.backtrack(s, index + 2, length)   # 能组成两位数

4. 打印出所有方案的代码

看题的时候就在想,我能不能把所有可能的结果都输出呢?答案当然是可以的,可以理解为回溯呀,在上面的代码上做小小的改动即可——

(1)我们使用一个 hash_map 来建立(0->‘a’,1->‘b’,……,25->‘z’)这些数字到相应字母之间的映射,可以直接写出来,也可以通过循环完成。
(2)然后在递归函数里加入一个 track,保存相应的路径值,剩下的跟上面是一模一样啦 ^ _ ^

class Solution:
    def translateNum(self, num):
        self.hash_map, self.res = {}, []
        s = str(num)
        for i in range(26):
            self.hash_map[i] = chr(97 + i)
        self.backtrack(s, '', 0, len(s))
        return self.res

    def backtrack(self, s, track, index, length):
        if index == length:
            # print(track)
            self.res.append(track)
            return
        a = ord(s[index]) - ord('0')
        self.backtrack(s, track + self.hash_map[a], index + 1, length)
        if index < length - 1:
            b = a * 10 + ord(s[index+1]) - ord('0')
            if b >= 10 and b <= 25:
                self.backtrack(s, track + self.hash_map[b], index + 2, length)

if __name__ == '__main__':
    t = Solution()
    num = 12258
    print(t.translateNum(num))
输出:['bccfi', 'bczi', 'bwfi', 'mcfi', 'mzi']

二、【LeetCode 91】解码方法

1. 题目描述

一条包含字母 A-Z 的消息通过以下方式进行了编码:

‘A’ -> 1
‘B’ -> 2

‘Z’ -> 26

给定一个只包含数字的非空字符串,请计算解码方法的总数。

示例 1:

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

示例 2:

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

2. 出错的思路

乍一看,这不是和上一题【把数字翻译成字符串】一模一样吗?很激动的敲完了代码,再一细看发现有点不一样,上一题的翻译的数字范围是0~25, 而本题是从1~26,也就是说若0单独出现,或者出现在开头都是不算的。想了一下,在处理一位数时加了一个判断条件:if a >= 1,这样就能保证数字是从1开始的了。

class Solution:
    def numDecodings(self, s: str) -> int:
        if not s:
            return 0
        self.res = 0
        nums = list(s)
        self.backtrack(nums, 0)
        return self.res

    def backtrack(self, nums, index):
        if index == len(nums):
            self.res += 1
            return
        a = int(nums[index])  # 先处理一位数的情况
        if a >= 1:
            self.backtrack(nums, index+1)
        if index+1 < len(nums):
            b = a * 10 + int(nums[index+1])
            if b >= 10 and b <= 26:
                self.backtrack(nums, index+2)

然而事情并没有我想象的那么简单,开心的提交了之后我惊讶的发现,居然超时了!!!
在这里插入图片描述
所以回溯行不通,还是乖乖地用动态规划吧~

3. 动态规划求解

class Solution:
    def numDecodings(self, s: str) -> int:
        if not s:
            return 0
        n = len(s)
        dp = [0 for i in range(n + 1)]
        if s[0] == '0':
            return 0
        dp[0], dp[1] = 1, 1
        for i in range(1, n):
            if s[i] != '0':
                dp[i+1] = dp[i]
            num = 10 * int(s[i-1]) + int(s[i])
            if 10 <= num <= 26:
                dp[i+1] += dp[i-1]
        return dp[n]

参考国际站更简洁一点的写法:

class Solution:
    def numDecodings(self, s: str) -> int:
        if not s:
            return 0
        n = len(s)
        dp = [0 for i in range(n + 1)]
        if s[0] == '0':
            return 0
        dp[0], dp[1] = 1, 1
        for i in range(2, n+1):
            if int(s[i-1:i]) > 0:
                dp[i] = dp[i-1]
            if 10 <= int(s[i-2:i]) <= 26:
                dp[i] += dp[i-2]
        return dp[n]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值