分数加减运算

标签: 数学 字符串 模拟

难度: Medium

给定一个表示分数加减运算的字符串 expression ,你需要返回一个字符串形式的计算结果。 

这个结果应该是不可约分的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1。所以在上述例子中, 2 应该被转换为 2/1

示例 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"

提示:

  • 输入和输出字符串只包含 '0' 到 '9' 的数字,以及 '/', '+' 和 '-'。 
  • 输入和输出分数格式均为 ±分子/分母。如果输入的第一个分数或者输出的分数是正数,则 '+' 会被省略掉。
  • 输入只包含合法的最简分数,每个分数的分子分母的范围是  [1,10]。 如果分母是1,意味着这个分数实际上是一个整数。
  • 输入的分数个数范围是 [1,10]。
  • 最终结果的分子与分母保证是 32 位整数范围内的有效整数。

Submission

运行时间: 28 ms

内存: 16.1 MB

class Solution:
    def fractionAddition(self, expression: str) -> str:
        
        def gcd(x,y):
            while y:
                x, y = y, x%y
            return x
        
        def lcm(x,y):
            return (x*y)//gcd(x,y) 

        def add(num1, num2, sign):
            if num1[1] == num2[1]:
                return [num1[0]+sign*num2[0],num1[1]]
            else:
                cm = lcm(num1[1],num2[1])
                return [num1[0]*(cm//num1[1]) + sign*num2[0]*(cm//num2[1]), cm]

        tmp = ""
        res = [0,1]
        num = [0,1]
        sign = 1
        idx = 0
        for c in expression:
            if c == '+' or c == '-':
                if tmp != '':
                    num[idx] = int(tmp)
                    tmp = ""
                    res = add(res, num, sign)
                    idx = 0
                if c == '+':
                    sign = 1
                else:
                    sign = -1
                
            elif c == '/':
                num[idx] = int(tmp)
                idx = 1 - idx
                tmp = ""
            else:
                tmp += c
        
        num[idx] = int(tmp)
        tmp = ""
        res = add(res, num, sign)
        cd = gcd(res[0], res[1])

        return "%d/%d" % (res[0]//cd, res[1]//cd)

Explain

这个题解的思路是模拟分数加减运算的过程。首先定义了求最大公约数(gcd)和最小公倍数(lcm)的函数。然后定义了分数加法函数add,根据两个分数的分母是否相同来进行不同的处理。接着遍历给定的表达式,用一个临时变量 tmp 来记录当前处理的数字。遇到 '+' 或 '-' 时,根据前面的符号对当前分数进行加减运算,并更新符号。遇到 '/' 时,将当前数字作为分子,并切换到分母。最后,将表达式中最后一个分数也加入计算,并对结果进行约分。

时间复杂度: O(nlogM),其中 n 是表达式的长度,M 是分母的最大值。

空间复杂度: O(1)

class Solution:
    def fractionAddition(self, expression: str) -> str:
        
        def gcd(x,y):
            while y:
                x, y = y, x%y
            return x
        
        def lcm(x,y):
            return (x*y)//gcd(x,y) 

        def add(num1, num2, sign):
            if num1[1] == num2[1]:
                return [num1[0]+sign*num2[0],num1[1]]
            else:
                cm = lcm(num1[1],num2[1])
                return [num1[0]*(cm//num1[1]) + sign*num2[0]*(cm//num2[1]), cm]

        tmp = ""
        res = [0,1]    # 存储最终结果
        num = [0,1]    # 存储当前处理的分数
        sign = 1       # 当前的运算符号
        idx = 0        # num中当前存储分子(0)还是分母(1)
        for c in expression:
            if c == '+' or c == '-':
                if tmp != '':    # 将当前分数加入结果
                    num[idx] = int(tmp)
                    tmp = ""
                    res = add(res, num, sign)
                    idx = 0
                if c == '+':     # 更新运算符号
                    sign = 1
                else:
                    sign = -1
                
            elif c == '/':
                num[idx] = int(tmp)    # 当前数字作为分子
                idx = 1 - idx          # 切换到分母
                tmp = ""
            else:
                tmp += c     # 记录当前数字
        
        num[idx] = int(tmp)    # 将最后一个分数加入结果
        tmp = ""
        res = add(res, num, sign)
        cd = gcd(res[0], res[1])    # 结果约分

        return "%d/%d" % (res[0]//cd, res[1]//cd)

Explore

在LeetCode等在线编程平台上编写解题代码时,通常优先选择简单和直观的解决方案以减少代码复杂度和提高执行效率。使用列表`[numerator, denominator]`直接表示分数是一种简单有效的方式,可以快速访问和修改分子和分母。此外,使用Python列表比创建一个自定义的分数类要少写更多的代码,并且在没有提供分数操作的内置库支持的情况下,使用列表可以更容易地进行分数的基本操作,如加减和乘除。

代码通过维护一个临时字符串`tmp`来收集当前数字,并使用`sign`变量来记录最近的运算符(+或-)。每次遇到新的运算符或表达式结束时,代码会将`tmp`中的内容转换为整数并更新到当前处理的分数`num`中,然后根据`sign`将这个分数与之前的结果`res`进行加减运算。这种方法确保了每次遇到运算符时都能将之前的分数正确地加入到总结果中,然后重置`tmp`和更新`sign`以准备下一个分数的处理。

使用最小公倍数(lcm)来处理不同分母的分数确实是一个有效的方法,因为它允许两个分数在相同的分母下进行加减运算。然而,这种方法可能会导致整数溢出,特别是当分母非常大时。在Python中,整数类型可以自动处理大数,但在其他一些语言中,如C++或Java,大数可能导致性能瓶颈或需要使用特定的库来处理大整数。因此,虽然这种方法在Python中通常是安全的,但在其他语言中可能需要额外的注意和处理。

在代码中,`sign`变量默认初始化为1。当解析表达式时,如果表达式的第一个字符是负号`-`,则在遇到这个负号时将`sign`设置为-1。这样,当读取到第一个分数的数字时,它会根据这个`sign`被正确地解析为负数。这种处理确保了即使表达式以负号开始,第一个分数也能被正确地解析和处理。