持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情
题目链接:592. 分数加减运算
题目描述
给定一个表示分数加减运算的字符串 expression ,你需要返回一个字符串形式的计算结果。
这个结果应该是不可约分的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1。所以在上述例子中, 2 应该被转换为 2/1。
提示:
- 输入和输出字符串只包含
'0'到'9'的数字,以及'/','+'和'-'。 - 输入和输出分数格式均为
±分子/分母。如果输入的第一个分数或者输出的分数是正数,则'+'会被省略掉。 - 输入只包含合法的 最简分数,每个分数的 分子 与 分母 的范围是
[1,10]。 如果分母是1,意味着这个分数实际上是一个整数。 - 输入的分数个数范围是
[1,10]。 - 最终结果 的分子与分母保证是
32位整数范围内的有效整数。
示例 1:
输入: expression = "-1/2+1/2"
输出: "0/1"
示例 2:
输入: expression = "-1/2+1/2+1/3"
输出: "1/3"
示例 3:
输入: expression = "1/3-1/2"
输出: "-1/6"
整理题意
题目给定一个表示分数加减运算的字符串 expression,我们需要返回一个字符串形式的计算结果。
需要注意的是返回的计算结果需要是约分后的结果,如果约分后是个整数也要返回分数的形式,分母写成 1 即可。
解题思路分析
由于该题给定的字符串一定是分数加减的表达式,并且只有 + 和 - 运算,所以整个表达式一定是 a/b + ... + c/d 的形式,所以我们可以遍历字符串,每次取出当前分数的分子和分母,然后与之前的答案进行运算,最后将分子分母约分后返回答案即可。
对于两个分数的加法运算为:
具体实现
- 初始化分子为
0,分母为1,表示最开始的值为0; - 遍历字符串以除号
'/'作为分界线,每次取出当前字符串的分子和分母; - 与之前的计算结果进行加法运算。
- 利用最大公约数对最终的答案进行约分化简,这里需要注意分子可能为负数,所以在求最大公约数的时候对分子进行取绝对值处理。
需要注意最后答案如果分子为 0 的话是无法进行约分的,这里要特判这种情况:当最终结果分子为 0,那么就直接输出 "0/1"。
需要注意的是根据题目数据范围的提示,只说明了 最终结果 的分子与分母在 int 整型范围内,为了防止中间运算溢出我们还是使用 long long int 整型进行存储。
由于题目给定的表达式时字符串的形式,我们这里可以使用 stoll() 函数将字符串转换为 long long int 整型(stoi()->int、stol()->long int),同时在输出答案可以使用 to_string() 函数进行转换,可以避免字符串与整型之间的转换代码的冗长。
复杂度分析
- 时间复杂度:,其中
n是字符串expression的长度,C为化简前结果分子分母的最大值。求最大公约数需要 。 - 空间复杂度:
代码实现
class Solution {
private:
// 最大公约数 也可以直接调用#include<algorithm>库函数 __gcd(a, b);
int GCD(int a, int b){
return b == 0 ? a : GCD(b, a % b);
}
/* 联想最小公倍数(本题用不到)
int LCM(int a, int b){
return a * b / GCD(a, b);
}
*/
public:
string fractionAddition(string expression) {
// 题目数据范围只保证最终结果没有溢出,防止中途溢出开 long long
long long int numerator = 0, denominator = 1; //分子,分母
int n = expression.length(), i = 0;
while(i < n){
// 读取分子
string temp = "";
while(i < n && expression[i] != '/') temp += expression[i++];
long long int numerator1 = stoll(temp);
i++;
// 读取分母
temp = "";
while(i < n && isdigit(expression[i])) temp += expression[i++];
long long int denominator1 = stoll(temp);
// 通分
numerator = denominator * numerator1 + denominator1 * numerator;
denominator = denominator * denominator1;
}
if(numerator == 0) return "0/1";
// 最大公约数,需要注意分子的负号
int g = GCD(abs(numerator), denominator);
return to_string(numerator / g) + '/' + to_string(denominator / g);
}
};
总结
- 该题核心为 数学 和 字符串处理,在知道分数的运算法则后,剩下的就是字符串的处理和代码的实现了,也就是 暴力模拟 即可。
- 使用手写的辗转相除法的
GCD(a, b)函数比调用库函数__gcd(a, b)在运行速度上要快一点,所以更推荐手写辗转相除法的GCD(a, b)。 - 测试结果:
结束语
成功往往在多次失败之后才姗姗到来。每一次跌倒后重新站起来,都能使你的本领更强大,信心更充足。不要轻言放弃,持之以恒去做事,当挫折在脚下堆积成梯,你也就获得了进步的机会。新的一天,加油!