【问题描述】
将非负整数num转换为对应的英文表达式。
(样例1)
输入:25
输出:Twenty Five
(样例2)
输入:12,315
输出:Twelve Thousand Three Hundred (and) Fifteen
备注:and可省略
另备注:偶然发现这个算法问题,并进行了尝试,当时并不知道是力扣原题(Q273,官网给出的num其范围为0~2^31 - 1),故从严谨性和通用性考虑,假定了num为64位无符号整数。
【思考过程】
按数学定义,num为非负整数集合(包含0),虽然题设没有给出整数num的取值上限,但考虑到题解的通用性可将num的取值上限定为2^63 - 1。另外,还需要了解数字对应的英文如何表达(特别是一些较大的数字),为此可参照下面的表格总结出表达规律:
Number | In English Phrase | Notes |
0 | Zero | |
25 | Twenty Five | |
127 | One Hundred (and) Twenty Seven | (and)可省略 |
1,024 | One Thousand (and) Twenty Four | (and)可省略 |
12,315 | Twelve Thousand Three Hundred (and) Fifteen | (and)可省略 |
589,001 | Five Hundred (and) Eighty Nine Thousand (and) One | |
16,000,014 | Sixteen Million (and) Fourteen | |
1,000,000,000 | One Billion | |
9,223,372,036,854,775,807 = (2^63 - 1) | Nine Quintillion , Two Hundred Twenty Three Quadrillion , Three Hundred Seventy Two Trillion , Thirty Six Billion , Eight Hundred Fifty Four Million , Seven Hundred Seventy Five Thousand , Eight Hundred Seven | Quintillion:1018 Quadrillion:1015 Trillion:1012 Billion:109 Million:106 Thousand: 103 |
很容易观察到,表达时从最低数位开始(按每三位一组进行划分,不足三位就取实际位数)依次表达到最高数位为止,具体理解如下:
每三位一组(不够三位的取实际位数),记a,b,c分别为百位、十位、个位上对应的数字,三者组合起来可以得到实际数值为0~999的任意整数,故我们只需要将该实际数值表达为英语即可,同时还需要加上数量级的表达(如thousand,million之类的)。
现在可以来考虑实际编码工作:对于0~19,20、30、40、...、90这些数字,对应的英文表达可提前用字符串数组存放起来,并将这部分的数字解析工作交由to_single_digit()与to_double_digit()两函数来完成(这两个函数共同处理num < 100的数字);对于100及以上的数字,按每三位一组进行表达,同时还需要加上当前数量级的表达(数量级的英语表达可用哈希表unordered_map容器存放起来,以便编程时的直接引用),具体而言,这部分的数字解析工作交由to_complex_digit()函数来完成。
在to_complex_digit()函数中,应循环地去处理每个数字分组,将其翻译表达为对应的英文。因为我们是从低位数字分组一直处理到高位数字分组,故需要同时用到队列和栈这两种数据结构。其中,队列中存放的是某个数字分组对应的英文表达(且队列中不会同时存放多个数字分组对应的英文表达),而栈中会依次压入之前处理好的每组英文表达(以便最后陆续出栈时,拼接为最终整体的英文表达结果)。
[代码实现]
#include<iostream>
#include<string>
#include<queue>
#include<stack>
#include<unordered_map>
#include<cmath>
using namespace std;
using ull = unsigned long long;
class NumToEnglish
{
private:
string ans;
string table_0_19[20] = {"Zero", "One", "Two", "Three",
"Four", "Five", "Six", "Seven",
"Eight", "Nine", "Ten", "Eleven",
"Twelve", "Thirteen", "Fourteen", "Fifteen",
"Sixteen", "Seventeen", "Eighteen", "Nineteen"};
string table_20_90[8] = {"Twenty", "Thirty", "Forty", "Fifty",
"Sixty", "Seventy", "Eighty", "Ninety"};
unordered_map<ull, string> table_sepcial = {
{1e3, "Thousand"}, {1e6, "Million"},
{1e9, "Billion"}, {1e12, "Trillion"}, {1e15, "Quadrillion"},
{1e18, "Quintillion"}
};
//将队列中的内容转换为一个字符串
string queue_to_string(queue<string>& Q)
{
string res;
while(!Q.empty())
{
res.append(Q.front());
Q.pop();
}
return res;
}
//将栈中的内容转换为一个字符串
string stack_to_string(stack<string>& S)
{
string res;
while(!S.empty())
{
res.append(S.top());
S.pop();
}
return res;
}
string to_single_digit(int num) //0~19
{
return table_0_19[num];
}
string to_double_digit(int num) //20~99
{
string res;
if(num < 20)
{
res = to_single_digit(num);
}
else
{
int sw = num / 10;
int gw = num % 10;
res.append(table_20_90[sw - 2]);
if(gw != 0)
{
res.append(" ");
res.append(table_0_19[gw]);
}
}
return res;
}
string to_complex_digit(ull num) //100~2^63 - 1
{
queue<string> qu;
stack<string> st;
string tmp = to_string(num); //转换为string, 每三位一组解析数据
string res;
//i: 指向每组数据的开头, pre_start: 指向上一组数据的开头
int len = tmp.size();
int i = len - 3, pre_start = len, bw, sw, gw;
ull factor = 1; //数量级标识: thousand, million... etc
while(i >= 0)
{
int sublen = pre_start - i; //每组数据的长度
bool zero_flag = false; //每组数据是否全为0的标志
//根据每组数据的长度, 来计算出英文表达
if(sublen == 3)
{
bw = tmp[i] - '0';
sw = tmp[i + 1] - '0';
gw = tmp[i + 2] - '0';
sw = sw * 10 + gw; //将每组的个位和十位合并后再处理
if(bw != 0)
{
qu.emplace(to_single_digit(bw)), qu.emplace(" ");
qu.emplace("Hundred");
if(sw != 0)
{
qu.emplace(" "), qu.emplace(to_double_digit(sw));
}
}
else
{
if(sw != 0) qu.emplace(to_double_digit(sw));
}
zero_flag = !(gw || sw || bw);
}
else if(sublen == 2)
{
sw = tmp[i] - '0';
gw = tmp[i + 1] - '0';
sw = sw * 10 + gw; //将每组的百位和十位合并后再处理
if(sw != 0) qu.emplace(to_double_digit(sw));
zero_flag = !(gw || sw);
}
else //sublen == 1
{
gw = tmp[i] - '0';
qu.emplace(table_0_19[gw]);
zero_flag = !gw;
}
//加上当前数量级的英文表达
if(factor != 1 && zero_flag != true)
{
qu.emplace(" " + table_sepcial[factor]);
}
if(!qu.empty()) st.emplace(queue_to_string(qu) + " "); //将当前这组处理数据压入栈中
//若还有剩余的高位组数据, 则更新i, pre_start, factor的取值
if(i == 0) break;
factor *= 1e3; //扩大数量级, 以便处理下一组数据
pre_start = i;
if(i - 3 >= 0) i -= 3;
else if(i - 2 >= 0) i -= 2;
else if(i - 1 >= 0) --i;
}
res = stack_to_string(st);
return res;
}
public:
string transform(ull num) //用户接口(将数字转为英文表达)
{
if(num < 100)
{
ans = to_double_digit(num);
}
else
{
ans = to_complex_digit(num);
}
return ans;
}
};
int main()
{
ull num;
cin >> num;
NumToEnglish nte;
cout << nte.transform(num) << endl;
return 0;
}
[测试结果]