题目描述

:::fold-green 相关原题

牛客原题: ✅ 阿拉伯数字转中文(含负数)

牛客原题转换整数并支持负数;本文转换非负十进制字符串,小数部分逐位读出。

:::

给定非负十进制数字字符串 input,把整数部分转为中文读法,小数部分逐位读出并保留末尾零。

示例 1:

输入: input = "10.050"
输出: "十点零五零"

提示:

  • 整数部分在 [0,2147483647]。
  • 最多一个小数点,小数点两侧非空,只含 ASCII 数字。
  • 不使用浮点转换。

题意分析

整数需要按位权和四位分节来读,小数却必须逐位读出,不能套用同一套规则。输入中的末尾小数零也是输出的一部分,因此全程保留字符串形式,只将整数段解析为整数。

解法:整数分节 + 小数逐位映射

核心思路

[!blue]

先把输入当文本按小数点分成两部分。小数部分要保留每一位,所以不能先转浮点数:10.050 与 10.05 数值相同,但本题读法不同。

整数部分复用四位分节:按亿、万、个拆组,每组按千、百、十、个输出,连续零只在后面还有非零数字时补一次;跨空组或低组不足四位时也要补零。整串以“一十”开头时才省略开头的“一”。

小数部分则逐字符查零到九的名称,在两部分之间加“点”。例如 10.050 的整数部分为“十”,小数部分依次为“零五零”,合成“十点零五零”。

解题步骤

  1. 按小数点拆分,检查数字格式,并解析整数部分。
  2. 把整数拆成亿、万、个三组,从高组到低组转换,每组按千、百、十、个位输出。
  3. 用待补零标记合并组内连续零,跨空组或低组不足四位时在组间补零;仅省略整串开头的一十中的一。
  4. 存在小数部分时追加点,再逐位映射数字,包括末尾零。

代码实现

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. 阿拉伯数字转中文数字 中等 整数部分复用四位分节的中文读数规则;该题只转换非负整数,本题还处理小数,小数部分逐位读取并保留零。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/787513866685
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!