[字符串 数学] 273. 整数转换英文表示(分组转换)
273. 整数转换英文表示
题目链接:https://leetcode-cn.com/problems/integer-to-english-words/
分类:
- 数学(给定数字转英文的规则、数字分组转换:从最低位开始每3位分成一组进行转换)
- 字符串(设置字符串数组:THOUSAND,LESS_THAN_TWENTY,HUNDRED作为字典数组、转换细节:空格符的处理)
思路:分组转换
理解转换规则:
int型最大数是:2,147,483,647,共10位。
整个数字可以按逗号划分为多个部分,每个部分都按三位数进行英文转换:
例如 147:One Hundred Forty Seven,最后再在后面加上量级:One Hundred Forty Seven Million
三位数中含0时的转换:
例如 103:one hundred three,而不是one hundred and three
转换前所需要的准备:
为了转换方便,我们先将0~19,每个整十,三个量级都预先存放在数组中,作为字典数组供查询使用:
private final String[] THOUSAND = {"", "Thousand", "Million", "Billion"};
private final String[] LESS_THAN_TWENTY = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
private final String[] HUNDRED = {"", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"};
其中,LESS_THAN_TWENTY数组存放的是0~19对应的英语,且0对应的是空字符串"";
同理HUNDRED数组中0和10对应的英语也设置为空字符串,因为10对应的ten已经在
LESS_THAN_TWENTY中了。
转换的具体流程:
对整个数字我们可以分为多个部分来处理,例如:2,147,483,647 从最低位开始每3位数分为一组,可以分为4个分组,每个分组最多3位数字,分组的内部处理都是相同的,内部处理结束后返回对应的英文字符串,将这4个分组对应的英文字符串组合起来,就能得到最终的转换结果。
从整体上看,我们每次处理的分组就是num的最低3位(即 num % 1000),然后将num的int形式向右移动3位(即num / 1000),再处理当前num的最低3位,直到num == 0。
在最终转换结果res中,越先处理的3位数字得到的英文字符串在res中排在越后面:
例如:num = 2,147,483,647
最先处理num的低3位647(num % 1000),得到"Six Hundred Forty Seven",res="Six Hundred Forty Seven"
num右移3位:num / 1000 = 2 147 483
接着处理num的低3位483,得到"Four Hundred Eighty Three Thousand",需要前插到res的首部,即res="Four Hundred Eighty Three Thousand Six Hundred Forty Seven"
...以此类推。
因此,我们需要使用StringBuilder的insert函数,将当前分组得到的英文字符串前插到res的0下标处:
res.insert(0, str);//向res的首部插入str
分组内部处理helper函数:
先开辟一个StringBuilder来存放分组转换的英语字符串;
例如:2,147,483,647
最先处理的分组是num = 647,我们从百位到个位依次处理:
第一阶段:获取百位上的数值:647/100=6,因为可能存在047这种百位为0的情况,所以只有百位数值 > 0才对百位做进一步处理:
- 将百位数值6转换成英语(通过LESS_THAN_TWENTY[6]获取),接着加上" Hundred "(注意Hundred前后的空格符不能省略);
- 将647去除百位:num = 647 % 100 = 47,进入下一阶段;
第二阶段:处理此时的num:
- 如果num >= 20,则模仿百位对十位做进一步处理:
- 将num的十位数值4(num/10=4)转换成英语(通过HUNDRED[4]获取"Forty");
- 将47去除十位:num = 47 % 10 = 7,进入第三阶段;
- 如果num < 20,则直接进入第三阶段:
第三阶段:将此时的num=7转换成英语(通过LESS_THAN_TWENTY[7]获取);
最后,在生成的英语字符串末尾加上量级对应的英语:
- 647对应的量级是0,字符串末尾不需要加任何量级,所以把THOUSAND[0]设置为空字符串"";
- 484对应的量级是1,字符串末尾要加上"Thousand";
- 147对应的量级是2,字符串末尾要加上"Million";
- 2对应的量级是3,字符串末尾要加上"Billion"。
除了上述的主要流程,内部处理还有需要注意的细节:
-
如果需要转换的三位数全为0,则直接转换成空字符串,不需要将0转换成英语,也不需要加量级,
例如:1000000 输出:"One Million",前两个000都只转换成空字符串。
-
百位的"Hundred"之后、十位的英语之后、个位的英语之后、量级的英语之后都记得加上空格符,但这样可能导致整个num转换成英语字符串时末尾可能有多余的空格符,所以在返回最终结果前将尾部的空格符去掉:trim()。
实现代码:
class Solution {
private final String[] THOUSAND = {"", "Thousand", "Million", "Billion"};
private final String[] LESS_THAN_TWENTY = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
private final String[] HUNDRED = {"", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"};
public String numberToWords(int num) {
//特殊用例
if(num == 0) return "Zero";
//level用于记录当前三位数所在的量级(level=1:"Thousand", =2:"Million", =3:"Billion")
int level = 0;
//res存放数字num转换的英语字符串
StringBuilder res = new StringBuilder();
//每次处理num的低3位,然后将num右移3位,直到num==0
while(num != 0){
//获取当前num的低3位转换的英文字符串
String part = helper(num % 1000, level);
//将转换的英文前插到res首部(insert的使用是关键)
res.insert(0, part);
//每处理完3位数字,量级+1
level++;
//num的int型右移3位
num /= 1000;
}
//最后返回前要去除首尾可能存在的空格符
return res.toString().trim();
}
//对每部分(最多3位数字)做内部处理,返回该部分对应的英文,level表示num这三位数所在的量级
public String helper(int num, int level){
StringBuilder sb = new StringBuilder();
//如果num为0,则直接返回空字符串
if(num == 0) return sb.toString();
//如果num的百位不为0,则做百位数值的转换
if(num / 100 > 0) {
sb.append(LESS_THAN_TWENTY[num / 100]).append(" Hundred ");
num %= 100;
}
//如果此时num >= 20,则对十位数值做转换,然后提取num的个位数
if(num >= 20){
sb.append(HUNDRED[num / 10]).append(" ");
num %= 10;//获取num的个位数
}
//如果此时num < 20,则直接做英文转换
if(num > 0){
sb.append(LESS_THAN_TWENTY[num]).append(" ");
}
//最后在英文字符串末尾加上当前num所在的量级的英文
sb.append(THOUSAND[level]).append(" ");
return sb.toString();
}
}