题目难度: 困难
今天继续更新程序员面试金典系列, 大家在公众号 算法精选 里回复 面试金典 就能看到该系列当前连载的所有文章了, 记得关注哦~
题目描述
给定一个整数,打印该整数的英文描述。
示例 1:
- 输入: 123
- 输出: "One Hundred Twenty Three"
示例 2:
- 输入: 12345
- 输出: "Twelve Thousand Three Hundred Forty Five"
示例 3:
- 输入: 1234567
- 输出: "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"
示例 4:
- 输入: 1234567891
- 输出: "One Billion Two Hundred Thirty Four Million Five Hundred Sixty Seven Thousand Eight Hundred Ninety One"
题目思考
- 需要考虑哪些情况?
解决方案
思路
- 观察题目, 我们可以发现, 数字的英文表示有这样的规律: 从个位开始, 每三个数字为一组, 可以使用相同的方式转换; 然后后面跟上当前的单位, 如 Thousand/Million
- 所以我们可以模拟这个过程, 从右向左遍历, 每 3 位数字一组, 将其转换成英文表示并添加单位的过程提取出来
- 具体细节方面, 我们需要考虑以下几种情况:
- 0 : 只有单独 0 才需要输出 Zero; 其他情况下遇到 0 都不输出 Zero
- 个位: 最简单的情况, 每个数字可以映射为一个单独的单词
- 十位: 需要额外构建 10~19 以及 20,30,...,90 与对应英文单词的映射关系
- 百位: 非 0 时, 需要额外添加 Hundred
- 千位/百万位/十亿位: 非 0 时, 需要额外添加 Thousand/Million/Billion
- 实现方面, 我们可以:
- 维护一个统一的数字到单词的映射字典, 包含 0
9, 1019 以及 20,30,...,90 与对应英文单词的映射关系 - 维护当前位数下标, 用于判断是否构成 3 位一组
- 维护当前的千位单位下标以及对应的单词
- 将转换 3 位一组数字到对应英文表示并添加单位的过程提取成函数, 里面单独处理百十个位
- 结果先输出到字符串数组, 最终再统一用空格隔开, 避免额外的空格处理
- 特别注意循环结束时累积的数字可能不足 3 位, 它们也需要转成字符串
- 维护一个统一的数字到单词的映射字典, 包含 0
- 下面代码就对应了上面的整个过程, 且每一步有详细的注释, 方便大家理解
复杂度
- 时间复杂度
O(1)
: 只遍历了常数长度的数字 - 空间复杂度
O(1)
: 只使用了几个常数空间的变量
代码
class Solution:
def numberToWords(self, num: int) -> str:
# maps存储数字和对应单词的映射关系, 注意10~19以及20~90的映射
maps = {0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine", 10: "Ten", 11: "Eleven", 12: "Twelve", 13: "Thirteen", 14: "Fourteen", 15: "Fifteen", 16: "Sixteen", 17: "Seventeen", 18: "Eighteen", 19: "Nineteen", 20: "Twenty", 30: "Thirty", 40: "Forty", 50: "Fifty", 60: "Sixty", 70: "Seventy", 80: "Eighty", 90: "Ninety"}
# units存储千位单位
units = [[], ["Thousand"], ["Million"], ["Billion"]]
if num == 0:
# 单独0, 直接返回Zero, 其他情况下都不会输出Zero
return maps[0]
def convertToList(num, ui):
# 独立方法, 将一组三位数字转成对应的英文表示, 并加上相应千位单位
if num == 0:
# 注意如果num是0的话直接返回空, 不管ui是什么, 否则1000000会被转换成One Million Thousand
return []
# 先转成[百,十,个]数组
nums = [num // 100 % 10, num // 10 % 10, num % 10]
res = []
for i, x in enumerate(nums):
if x == 0:
# 遇到0, 直接跳过
continue
if i == 1:
# 当前是十位
if x == 1:
# 10~19, 合并当前位和下一位的数字, 使用Ten到Nineteen, 退出循环
res.append(maps[x * 10 + nums[i + 1]])
break
else:
# 20+, 使用Twenty到Ninety
res.append(maps[x * 10])
else:
# 百位和个位都直接使用单个单词
res.append(maps[x])
if i == 0:
# 百位, 需要额外加上Hundred
res.append("Hundred")
# 注意额外加上当前的千位单位
return res + units[ui]
# i是当前的位数下标
i = 0
# ui是当前的千位单位下标, 第一组是空, 第二组是Thousand, 第三组是Million, 依此类推
ui = 0
# cur保存当前处理的数字
cur = 0
res = []
while num:
# 将当前数字累加到cur
cur += (num % 10) * (10 ** (i % 3))
num //= 10
i += 1
if i % 3 == 0:
# 此时达到分界点, 将当前的三位数字作为一组, 转换成字符串
res = convertToList(cur, ui) + res
# 注意更新千位单位下标和cur
ui += 1
cur = 0
# 注意循环结束时cur有可能不是0, 但是没达到3位, 也需要转成字符串
res = convertToList(cur, ui) + res
# 将字符串数组中间加上空格, 即为最终结果
return " ".join(res)
大家可以在下面这些地方找到我~😊
我的公众号: 算法精选, 欢迎大家扫码关注~😊