一、题目描述
句子 是由若干个单词组成的字符串,单词之间用单个空格分隔,其中每个单词可以包含数字、小写字母、和美元符号 '$'
。如果单词的形式为美元符号后跟着一个非负实数,那么这个单词就表示一个价格。
- 例如
"$100"
、"$23"
和"$6.75"
表示价格,而"100"
、"$"
和"2$3"
不是。
注意: 本题输入中的价格均为整数。
给你一个字符串 sentence
和一个整数 discount
。对于每个表示价格的单词,都在价格的基础上减免 discount%
,并 更新 该单词到句子中。所有更新后的价格应该表示为一个 恰好保留小数点后两位 的数字。
返回表示修改后句子的字符串。
示例 1:
输入:sentence = "there are $1 $2 and 5$ candies in the shop", discount = 50
输出:"there are $0.50 $1.00 and 5$ candies in the shop"
解释:
表示价格的单词是 "$1" 和 "$2" 。
- "$1" 减免 50% 为 "$0.50" ,所以 "$1" 替换为 "$0.50" 。
- "$2" 减免 50% 为 "$1" ,所以 "$1" 替换为 "$1.00" 。
示例 2:
输入:sentence = "1 2 $3 4 $5 $6 7 8$ $9 $10$", discount = 100
输出:"1 2 $0.00 4 $0.00 $0.00 7 8$ $0.00 $10$"
解释:
任何价格减免 100% 都会得到 0 。
表示价格的单词分别是 "$3"、"$5"、"$6" 和 "$9"。
每个单词都替换为 "$0.00"。
提示:
1 <= sentence.length <= 105
sentence
由小写英文字母、数字、' '
和'$'
组成sentence
不含前导和尾随空格sentence
的所有单词都用单个空格分隔- 所有价格都是 正 整数且不含前导零
- 所有价格 最多 为
10
位数字 0 <= discount <= 100
二、思路分析
拿到这道题的时候,可以看出来,只是替换题目中的数字,做起来并不难,但是看通过次数会发现通过率并不高。
首先从题目信息我们可以发现,题目中的数字都是整数,但是因为涉及到除法的操作,那么就出现精度的问题,这也是这个题中容易出现的坑。在Java中我们可以使用BigDecimal
类做乘法和除法操作,避免出现精度丢失。
接着我们分析解题步骤:
- 先把句子按照空格做分割,存放字符串数组中
- 遍历字符串数组,判断字符串是否是
$
的开头的,如果是跳转到3
,如果不是跳转到4
- 如果是
$
开头的字符串,开始遍历$
后面的字符,一边遍历一边添加到数字字符串中。如果遍历过程中发现非数字字符,可以直接把整个字符返回。如果遍历到最后发现都是数字,则把整个数字字符串转化成BigDecimal
类,做加减乘除的计算。把计算后的结果转化成字符串添加到结果字符串中。 - 如果不是
$
开头的字符串,直接把整个字符串添加到结果字符串中 - 字符串数组中的字符串遍历完以后,相当于整个字符串遍历结束。
这里有一个需要注意点,如果是单个$
的长度,按照第3步的处理过程,因为后面没有数字,就相当于空串转BigDecimal
类,会出错的。所以还需要判断字符串的长度哦。
示例代码
import java.math.BigDecimal;
import java.math.RoundingMode;
class Solution {
public String discountPrices(String sentence, int discount) {
// 根据空格分割字符串
String[] arr = sentence.split(" ");
StringBuilder sb = new StringBuilder();
for (String str : arr) {
// 如果是$开头的,并且字符串长度大于1,则判断是否需要做折扣计算
if (str.startsWith("$") && str.length() > 1) {
sb.append(processStr(str, discount));
} else {
sb.append(str);
}
sb.append(" ");
}
// 删除最后一个空格
return sb.deleteCharAt(sb.length() - 1).toString();
}
public String processStr(String str, int discount) {
// 计算后的字符串
String result = "$";
StringBuilder num = new StringBuilder();
for (int i = 1; i < str.length(); i++) {
char c = str.charAt(i);
// 判断字符是否为数字
if (c >= 48 && c <= 57) {
num.append(c);
} else {
// 如果不是数字,可以直接把字符串返回
return str;
}
}
// 使用BigDecimal做高精度的计算
BigDecimal bigDecimal = new BigDecimal(num.toString());
return result + bigDecimal.subtract(bigDecimal.multiply(new BigDecimal(discount).divide(new BigDecimal(100))))
.setScale(2, RoundingMode.HALF_UP);
}
}
那么是不是还有其他的解法呢?
还有一些想法,尚未实现代码,感觉实现逻辑比较复杂。通过遍历字符串的每个字符,如果遇到$
字符,则开始处理后面的数组字符,到遇到空格前,是否全部为数字。如果遇到非数字,则不用做计算了,如果全部是数字则需要做计算。
如果一个连续字符串中有多个$
,比如123 $21$234$23
,要注意处理最后的$23
,防止把23
做了更新操作。
相比于第一种方法,这里需要对所有的字符做处理,第一种解法中,通过分割字符串,跳过了很多不能转化的字符,但是需要额外的空间消耗。
三、总结
1、在代码中牵扯到乘法和除法操作的,涉及到小数部分的,都需要考虑到精度的问题。
2、在处理字符串的时候还需要考虑特殊的场景,比如开头或者结尾的部分出现一些标志性的数字或者字符,这时候该如何处理?