LeetCode刷题-190709-扩展:阿拉伯数字和中文数字转换

前面刷过一个题,关于罗马字和阿拉伯数字转换,我之前的文章链接:
https://blog.csdn.net/sleepingboy888/article/details/89814335
最近在看《算法的乐趣》王晓华著,其中的“阿拉伯数字和中文数字”。这篇也是自己学习记录下。
阿拉伯数字转中文数字
具体的思路可以参考书中的段落,文章中只说重点的地方。中文数字中0的表述比较复杂,总结起来为一下三点:

  1. 以 10000 为小节,小节的结尾即使是 0 ,也不使用 “零”。例如:60000,6万
  2. 小节内(一个小节为 4 位数)两个非 0 数字之间要使用“零”。例如:504,五百零四
  3. 当小节的“千”位是 0 时,若本小节的前一小节无其他数字,则不用“零”,否则就要用“零”。50,五十;10050,一万零五十。

如果对这三条规则是否完善存有疑惑,可以在代码写完以后用测试用例来检查。
需要说明的名词:

  1. 节,中文数字一般以4位为一小段,也就是万分位,对应与英文中的千分位。
  2. 权,一节中每一位都有对于的值,例如:1234,1对于的权是1千。
  3. 节权,每一节都有自己的权,最低的4位可以理解为节权位空,例如:163481234,1亿6384万1234。特别说明,代码中节权只识别到万亿。
  4. 位权,每一位数字在一节中的对应的值,分别是:千,百,十,(个位为空)。

节权
比较麻烦的可能是“零”的处理,书中归纳总结的挺好的,我个人稍微变通下。根据日常习惯,其实是阿拉伯数字中连续的零只读一个零。调试代码过程中,个人总结:

  1. 值为零的时候就不需要考虑权,节权和位权都不需要。
  2. 不出现多个“零”连续,有种情况需要特殊考虑,一节的最后几位都是0不需要“零”,例如:100000,一十万
  3. 返回字符串第一个字符和最后一个字符不能为“零”,当然,输入为0直接返回“零”。
 public static string NumberToChinese(uint num)
 {
     string result = "";
     if (num == 0)
     {
         return "零";
     }
     uint _num = num;
     //string[] chn_str = new string[] { "零","壹","贰","叁","肆","伍","陆","柒","捌","玖"};
     //string[] unit_value = new string[] { "", "拾", "佰", "仟" };
     string[] chn_str = new string[] { "零","一", "二", "三", "四", "五", "六", "七", "八", "九" };
     string[] section_value = new string[] { "","万","亿","万亿"};
     string[] unit_value = new string[] { "", "十", "百", "千" };
     uint section = _num % 10000;
     for (int i = 0; _num != 0 && i < 4; i++)
     {
         if (section == 0)
         {
             //0不需要考虑节权值,不能出现连续的“零”
             if (result.Length > 0 && result.Substring(0, 1) != "零")
             {
                 result = "零" + result;
             }
             _num = _num / 10000;
             section = _num % 10000;
             continue;
         }
         result = section_value[i]+result;
         uint unit = section % 10;
         for (int j = 0; j<4 ; j++)
         {
             if (unit == 0)
             {
                 //0不需要考虑位权值,不能出现联系的“零”,每节最后的0不需要
                 if (result.Length > 0 && result.Substring(0, 1) != "零" && result.Substring(0, 1) != section_value[i])
                 {
                     result = "零" + result;
                 }
             }
             else
             {
                 result = chn_str[unit] + unit_value[j] + result;
             }
             section = section / 10;
             unit = section % 10;
         }
         _num = _num / 10000;
         section = _num % 10000;
     }
     if (result.Length > 0 && result.Substring(0, 1) == "零")
     {
         //清理最前面的"零"
         result = result.Substring(1);
     }
     return result;
 }

测试用例代码:

public Dictionary<uint, string> m_dic_test = new Dictionary<uint, string>
{
    { 0,"零" },
    { 1,"一" },
    { 2,"二" },
    { 3,"三" },
    { 4,"四" },
    { 5,"五" },
    { 6,"六" },
    { 7,"七" },
    { 8,"八" },
    { 9,"九" },
    { 10,"一十" },
    { 11,"一十一" },
    { 110,"一百一十" },
    { 111,"一百一十一" },
    { 100,"一百" },
    { 102,"一百零二" },
    { 1020,"一千零二十" },
    { 1001,"一千零一" },
    { 1015,"一千零一十五" },
    { 1000,"一千" },
    { 10000,"一万" },
    { 20010,"二万零一十" },
    { 20001,"二万零一" },
    { 100000,"一十万" },
    { 1000000,"一百万" },
    { 10000000,"一千万" },
    { 100000000,"一亿" },
    { 1000000000,"一十亿" },
    { 1000001000,"一十亿零一千" },
    //{ 1000001000,"一十亿一千" },
    { 1000000100,"一十亿零一百" },
    { 200010,"二十万零一十" },
    { 2000105,"二百万零一百零五" },
    { 20001007,"二千万一千零七" },
    { 2000100190,"二十亿零一十万零一百九十" },
    { 1040010000,"一十亿四千零一万" },
    { 200012301,"二亿零一万二千三百零一" },
    { 2005010010,"二十亿零五百零一万零一十" },
    { 4009060200,"四十亿零九百零六万零二百" },
    { 4294967295,"四十二亿九千四百九十六万七千二百九十五" }
};

[TestMethod]
public void TestNumberToChinese()
{
    foreach (var item in m_dic_test)
    {
        Assert.AreEqual(Solutions.NumberToChinese(item.Key), item.Value, "error");
    }
}

按照我的标准和书中的不太一样,例如:
{ 1000001000,“一十亿一千” }
这个测试用例不能通过,代码解析为:一十亿零一千,我个人感觉这种读法也能接受。这代码不能到万亿,如果需要识别到万亿级,可以把 uint 类型替换掉。

中文数字转阿拉伯数字
这个的思路相对容易一些,主要是分段和分节,需要注意的点:

  • “万亿”的特殊处理,这个是两个字的节权名。
  • “十”,“百”,“千”,和数字成套出现,要么是数字加上这写数,或者数字单独出现(一个节中的最低位)
  • 没有判断输入的字符串是否满足中文数字的表述规则,默认输入的字符串满足规则。
public static UInt64 ChineseToNumber(string chinese_str)
{
    UInt64 result = 0;
    Dictionary<string, UInt64> dic_section_number = new Dictionary<string, UInt64> {
        { "万",10000 },{ "亿",100000000 },{ "万亿",1000000000000 }
    };
    Dictionary<string, UInt64> dic_unit_number = new Dictionary<string, UInt64> {
        { "十",10 },{ "百",100 },{ "千",1000 }
    };
    Dictionary<string, UInt64> dic_chn_number = new Dictionary<string, UInt64> {
        { "零",0}, { "一",1}, { "二",2} ,{ "三",3},{ "四",4},{ "五",5},{ "六",6},{ "七",7},{ "八",8},{ "九",9}
    };
    if (chinese_str.Length == 0)
    {
        return 0;
    }
    UInt64 section_value = 0;
    for (int i = 0; i < chinese_str.Length; i++)
    {
        string cur_str = chinese_str.Substring(i, 1);
        if (dic_section_number.ContainsKey(cur_str))
        {
            //万亿的特殊处理
            if (cur_str == "万" && i + 1 < chinese_str.Length && chinese_str.Substring(i + 1, 1) == "亿")
            {
                result += section_value * dic_section_number["万亿"];
                i++;
            }
            else
            {
                result += section_value * dic_section_number[cur_str];
            }
            section_value = 0;
        }
        else
        {
            if(dic_chn_number.ContainsKey(cur_str))
            {
                if (i + 1 < chinese_str.Length && dic_unit_number.ContainsKey(chinese_str.Substring(i + 1, 1)))
                {
                    //和数字一起成对出现的 十,百,千
                    section_value += dic_chn_number[cur_str] * dic_unit_number[chinese_str.Substring(i + 1, 1)];
                    i++;
                }
                else
                {
                    section_value += dic_chn_number[cur_str];
                }
            }
        }
    }
    result += section_value;
    return result;
}

//测试函数
[TestMethod]
public void TestChineseToNumber()
{
    foreach (var item in m_dic_test)
    {
        Assert.AreEqual(Solutions.ChineseToNumber(item.Value), item.Key, "error");
    }
}

C# UnitTest 初使用

  1. 在工程中 Solution 上右键 ->Add-> New Project…
  2. 添加一个 (c#) Unit Test Project
  3. 在创建的 Unit Test 工程中把需要测试的工程添加到 Reference
  4. 在自动生成的测试代码中添加自己的测试函数,然后运行就可以了。记得 using namespace。
  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值