BASIC-20 VIP试题 数的读法
问题描述
Tom教授正在给研究生讲授一门关于基因的课程,有一件事情让他颇为头疼:一条染色体上有成千上万个碱基对,它们从0开始编号,到几百万,几千万,甚至上亿。
比如说,在对学生讲解第1234567009号位置上的碱基时,光看着数字是很难准确的念出来的。
所以,他迫切地需要一个系统,然后当他输入12 3456 7009时,会给出相应的念法: 十二亿三千四百五十六万七千零九
用汉语拼音表示为 shi er yi san qian si bai wu shi liu wan qi qian ling jiu
这样他只需要照着念就可以了。
你的任务是帮他设计这样一个系统:给定一个阿拉伯数字串,你帮他按照中文读写的规范转为汉语拼音字串,相邻的两个音节用一个空格符格开。
注意必须严格按照规范,比如说“10010”读作“yi wan ling yi shi”而不是“yi wan ling
shi”,“100000”读作“shi wan”而不是“yi shi wan”,“2000”读作“er qian”而不是“liang
qian”。输入格式
有一个数字串,数值大小不超过2,000,000,000。
输出格式
是一个由小写英文字母,逗号和空格组成的字符串,表示该数的英文读法。
事后总结:
蓝桥杯的这种复杂题一定不能慌。不是有多少难度,就是看能不能尽量少花费时间。思路要找准,想好了再动手,写的尽量清楚,不然调试修改都是自己的。
思路:
10000以下的数字读法是相同的。把这种读法写成一个函数 read()
。大于10000(4位)而小于1亿(8位),在标准 read()
之后再读出 “万”
,大于1亿(8位)的部分为 read() + “亿”
。输入数据不超过20亿。
因此,我们把输入数据作为字符串读入,并右起按照4位一组分割成子串,不足4位的补齐4位。
对于标准读法 read()
如果出现数字 0
的,需要特别考虑。
0
出现在整个大数的开头,也就是我们补齐的0
,不读;- 出现在一个4位子串,即
read()
的末尾,不读; - 出现在4位子串的前部、中部,无论连续出现几个
0
只读一次。
对于上 1. ,我们用全局变量 bool is_head
记录我们是否在大数的开头。对于 2. 3. ,我们采取在每次 read()
当中维护一个 bool is_preZero
的策略,记录上一个遇见的数位上是否为 0
,且仅当 read()
读到一个“非零位”时,读出一次 0
。
特别的,当大数以一次 read()
的十位数字 1
起头时(is_head = true
),e.g. 10 0000 , 10 0000 0000
, 读作“十万,十亿”,而非“一十万,一十亿”。
作为输出的严谨性,我们控制行末不要输出额外的空格,采用前置空格分隔。对于 is_head = true
时的第一次输出,不输出空格。
由于练习系统暂不支持C++11,最终版本有一定修改。
AC代码:
// 数字の読み方 <2e10
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
string b[] = {"ling", "yi", "er", "san", "si", "wu", "liu", "qi", "ba", "jiu", "shi"};
string p[] = {" qian", " bai", " shi", ""}; // 前置空格,个位不输出
string r[3];
bool is_head = true;
void read(string s, int flag) {
if (s.empty())
return;
while (s.length() < 4) { // 补齐
s = '0' + s;
}
// cout << s;
bool is_pre0 = false;
for (int i=0; i<4; ++i) {
char ch = s[i];
if (ch != '0') {
if (is_pre0 && !is_head) // 读 '0'
cout << ' ' << b[0];
if (is_head && i == 2 && ch == '1') { // 大数以十位'1'开头
if (!is_head) cout << ' ';
cout << "shi";
}
else {
if (!is_head) cout << ' ';
cout << b[ch - '0'] << p[i];
}
is_pre0 = false;
is_head = false;
}
else
is_pre0 = true;
}
switch (flag)
{
case 2:
cout << " yi";
break;
case 1:
cout << " wan";
break;
case 0:
break;
default:
break;
}
}
int main() {
string num;
cin >> num;
int n = num.size();
// 按4位切割
// 伪代码 思路 (其实是调试失败)
// string t;
// for (int i=0; i<n; ++i) { //tranc by yy-wwwww-gggg
// t += num[n - i - 1];
// if ((i + 1) % 4 == 0) {
// reverse(t.begin(), t.end());
// r[i / 4] = t;
// t = "";
// }
// }
// C++ 11
// for (int i=0; i<3; ++i) { // r[i]
// string t;
// for (int j=0; j<4; ++j) { // r[i][j]
// if (num.empty())
// break;
// t += *num.rbegin();
// num.pop_back();
// }
// reverse(t.begin(), t.end());
// r[i] = t;
// if (t.length() < 4) break;
// }
// Before C++11
string::reverse_iterator rit = num.rbegin();
for (int i=0; i<3; ++i) { // r[i]
string t;
for (int j=0; j<4; ++j) { // r[i][j]
if (rit == num.rend())
break;
t += *rit;
++rit;
}
reverse(t.begin(), t.end());
r[i] = t;
if (t.length() < 4) break;
}
// for (int i=2; i>=0; --i) {
// cout << r[i] << endl;
// }
read(r[2], 2);
read(r[1], 1);
read(r[0], 0);
cout << endl;
return 0;
}