记小米一面算法题——人民币大写金额转换

30分钟小米算法工程师一面,由于没有项目经历面试官给了一道算法题,现撸代码。毫无意外,快速码代码向来不是我的强项。

特此记录失败史

题目描述

根据人民币大写金额规范,将浮点数转换为人民币读法字符串需要注意以下几点: 1. 数字中间有连续一个或以上个 0 时,只写一个。如 ¥ 140,090.3,应写作人民币十四万零玖拾元叁角。 2. 角位非 0 时,后面可以不加。如 ¥ 1690.32,可以写作人民币壹仟陆佰玖拾元叁角[零]贰分。 3. 角位是 0,分位不是 0 时,大写金额后面应写。如 ¥ 325.04,应写为人民币叁佰贰拾元零肆分。 4. 角和分都是 0 时,后面应写上。如 ¥ 1234.00,应写为人民币壹仟贰佰叁拾肆元(整/正)。

注意:最小单位为

题解

中文数字读法以 4 个数字为一个组,辅以单位如万、亿、兆、京等。这里取到最高为亿。由于每组单位不同,一度不知如何处理。因为金额大小没有限制,所以数字位数可以很大,如果根据单位用if...else...语句,是行不通的。 后来发现,还是可以使用递归来解决问题。

至于的处理,可以设定字符串替换规则,使得处理后满足规范。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
ch_num = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
ch_num = dict(zip('0123456789', ch_num))

ch_unit = ['', '万', '亿']
ch_unit = dict(zip(range(3), ch_unit))

def convert_int(intPart):
""" 转换整数部分
intPart: str
return: str
"""
if len(intPart) <= 4:
# 若长度小于 4, 则补足到 4 位。
ans = [ch_num[c] for c in '0'*(4-len(intPart))+intPart]
if ans[-1] == ch_num['0']:
ans = '{}仟{}佰{}拾'.format(*ans[:-1])
else:
ans = '{}仟{}佰{}拾{}'.format(*ans)
# 处理中间有 0 的情况
ans = ans.replace('零仟', ch_num['0'])
ans = ans.replace('零佰', ch_num['0'])
ans = ans.replace('零拾', ch_num['0'])
# 处理中间连续多个 0,末尾有零的情况
ans = re.sub('零+', ch_num['0'], ans)
ans = re.sub('零+$', ch_unit[0], ans)
# 若开头有 0 且整数部分长度小于 4,则丢弃开头的’零‘(只有 1 个)。
return ans[4!=len(intPart):]
else:
# 金额数字长度可以写成 n = 4 * u + r
u, r = len(intPart) // 4, len(intPart) % 4
# 若 r = 0,则 u = u - 1,至少先处理开头 4 位。之后再看 u,
# 若 u > 2,则先处理不包括最后 8 位的部分,否则就先只处理开头 4 位。
i = r * (r != 0) + 4 * (max(u - 2 - (r == 0), 0) + (r == 0))
# 于是分成两部分递归处理 intPart[:i] 和 intPart[i:]
left, right = convert_int(intPart[:i]), convert_int(intPart[i:])
# 若左部分返回空字符串 '',则不加单位,即 j = 0 and ch_unit[j] = ''。
# 又因为最大单位为“亿”,所以 0 <= j <= 2
j = min(u - (r == 0), 2) if left else 0
# 得到单位
unit = ch_unit[j]
# 返回最终结果
return '{}{}{}'.format(left, unit, right)

def convert_decimal(decimalPart):
""" 处理小数部分,容易多了
decimalPart: str
return: str
"""
ans = [ch_num[c] for c in decimalPart + '0' * (2 - len(decimalPart))]
ans = '{}角{}分'.format(*ans)
ans = ans.replace('零角', ch_num['0'])
ans = ans.replace('零分', ch_unit[0])
ans = re.sub('零+$', ch_unit[0], ans)
# 因为使用递归处理整数部分,所以整数部分单位“元”放在这里处理。
return '元{}'.format(ans or '整')

def convert_RMB(money):
""" 将浮点数转换为大写人民币规范读法
money: str
return: str
"""
if '.' in money:
intPart, decimalPart = money.split('.')
else:
intPart, decimalPart = money, ch_unit[0]
return '人民币{}{}'.format(convert_int(intPart), convert_decimal(decimalPart))