LeetCode 补充题 200. 十进制数的中文读法
题目描述
:::fold-green 相关原题
牛客原题: ✅ 阿拉伯数字转中文(含负数)
牛客原题转换整数并支持负数;本文转换非负十进制字符串,小数部分逐位读出。
:::
给定非负十进制数字字符串
input,把整数部分转为中文读法,小数部分逐位读出并保留末尾零。
示例 1:
输入:
input = "10.050"
输出:"十点零五零"
提示:
- 整数部分在
[0,2147483647]。 - 最多一个小数点,小数点两侧非空,只含 ASCII 数字。
- 不使用浮点转换。
题意分析
整数需要按位权和四位分节来读,小数却必须逐位读出,不能套用同一套规则。输入中的末尾小数零也是输出的一部分,因此全程保留字符串形式,只将整数段解析为整数。
解法:整数分节 + 小数逐位映射
核心思路
[!blue]
先把输入当文本按小数点分成两部分。小数部分要保留每一位,所以不能先转浮点数:
10.050与10.05数值相同,但本题读法不同。整数部分复用四位分节:按亿、万、个拆组,每组按千、百、十、个输出,连续零只在后面还有非零数字时补一次;跨空组或低组不足四位时也要补零。整串以“一十”开头时才省略开头的“一”。
小数部分则逐字符查零到九的名称,在两部分之间加“点”。例如
10.050的整数部分为“十”,小数部分依次为“零五零”,合成“十点零五零”。
解题步骤
- 按小数点拆分,检查数字格式,并解析整数部分。
- 把整数拆成亿、万、个三组,从高组到低组转换,每组按千、百、十、个位输出。
- 用待补零标记合并组内连续零,跨空组或低组不足四位时在组间补零;仅省略整串开头的一十中的一。
- 存在小数部分时追加点,再逐位映射数字,包括末尾零。
代码实现
class Solution {
public String decimalToChinese(String input) {
if (!input.matches("[0-9]+([.][0-9]+)?")) {
throw new IllegalArgumentException("invalid decimal");
}
String[] parts = input.split("[.]", -1);
StringBuilder out = new StringBuilder(numberToChinese(Integer.parseInt(parts[0])));
if (parts.length == 2) {
out.append("点");
for (int i = 0; i < parts[1].length(); i++) {
out.append(DIGITS[parts[1].charAt(i) - '0']);
}
}
return out.toString();
}
private static final String[] DIGITS = {
"零",
"一",
"二",
"三",
"四",
"五",
"六",
"七",
"八",
"九"
};
private static final String[] UNITS = {
"千",
"百",
"十",
""
};
private static final String[] GROUPS = {
"",
"万",
"亿"
};
public String numberToChinese(int num) {
if (num == 0) {
return "零";
}
int[] parts = {
num % 10000,
num / 10000 % 10000,
num / 100000000
};
StringBuilder builder = new StringBuilder();
boolean needZero = false;
for (int group = 2; group >= 0; group--) {
if (parts[group] == 0) {
if (builder.length() > 0) {
needZero = true;
}
continue;
}
if (builder.length() > 0 && (needZero || parts[group] < 1000)) {
builder.append("零");
}
builder.append(convertGroup(parts[group])).append(GROUPS[group]);
needZero = false;
}
String result = builder.toString();
if (result.startsWith("一十")) {
result = result.substring(1);
}
return result;
}
private String convertGroup(int value) {
int[] divisors = {
1000,
100,
10,
1
};
StringBuilder builder = new StringBuilder();
boolean zeroPending = false;
for (int i = 0; i < divisors.length; i++) {
int digit = value / divisors[i];
value %= divisors[i];
if (digit == 0) {
zeroPending = builder.length() > 0;
continue;
}
if (zeroPending) {
builder.append("零");
zeroPending = false;
}
builder.append(DIGITS[digit]).append(UNITS[i]);
}
return builder.toString();
}
}
import (
"fmt"
"strconv"
"strings"
)
func decimalToChinese(input string) (string, error) {
parts := strings.Split(input, ".")
if len(parts) > 2 {
return "", fmt.Errorf("invalid decimal")
}
for _, part := range parts {
if part == "" {
return "", fmt.Errorf("invalid decimal")
}
for i := range part {
if part[i] < '0' || part[i] > '9' {
return "", fmt.Errorf("invalid decimal")
}
}
}
integer, err := strconv.ParseInt(parts[0], 10, 32)
if err != nil {
return "", err
}
var out strings.Builder
out.WriteString(numberToChinese(int(integer)))
if len(parts) == 2 {
out.WriteString("点")
for i := range parts[1] {
out.WriteString(chineseDigits[parts[1][i]-'0'])
}
}
return out.String(), nil
}
var chineseDigits = []string{
"零",
"一",
"二",
"三",
"四",
"五",
"六",
"七",
"八",
"九",
}
var chineseUnits = []string{
"千",
"百",
"十",
"",
}
var chineseGroups = []string{
"",
"万",
"亿",
}
func numberToChinese(num int) string {
if num == 0 {
return "零"
}
parts := []int{
num % 10000,
num / 10000 % 10000,
num / 100000000,
}
result := ""
needZero := false
for group := 2; group >= 0; group-- {
if parts[group] == 0 {
if len(result) > 0 {
needZero = true
}
continue
}
if len(result) > 0 && (needZero || parts[group] < 1000) {
result += "零"
}
result += convertChineseGroup(parts[group]) + chineseGroups[group]
needZero = false
}
if len(result) >= len("一十") && result[:len("一十")] == "一十" {
result = result[len("一"):]
}
return result
}
func convertChineseGroup(value int) string {
divisors := []int{
1000,
100,
10,
1,
}
result := ""
zeroPending := false
for i, divisor := range divisors {
digit := value / divisor
value %= divisor
if digit == 0 {
zeroPending = len(result) > 0
continue
}
if zeroPending {
result += "零"
zeroPending = false
}
result += chineseDigits[digit] + chineseUnits[i]
}
return result
}
复杂度分析
- 时间复杂度:设输入长度为 $L$,时间 $O(L)$。
- 空间复杂度:$O(L)$,包括输出。
关键点总结
[!green]
按小数点分割,整数部分复用分节读数;小数部分逐字符映射零到九,拼接时加入“点”。
易错点总结
[!yellow]
- 不经过浮点数转换,避免精度变化和末尾零丢失。
- 整数中的零只在后面还有非零数字时补读,不能给整十、整百追加尾零。
- 只省略整串开头的一十中的一,不能对内部每个分节都省略。
- 整数为 0 时仍需输出零,小数位则逐位保留。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 补充题 14. 阿拉伯数字转中文数字 | 中等 | 整数部分复用四位分节的中文读数规则;该题只转换非负整数,本题还处理小数,小数部分逐位读取并保留零。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!