Problem 972

Abstract / Summary

Given two pairs of integers representing fractions check if they represent the same rational number

Note

The full problem description, including helpful examples, can be found on LeetCode.

Python 0972.py

class Solution:
    def isRationalEqual(self, s: str, t: str) -> bool:
        def parse(x):
            integer  = x
            fraction = "0"
            repeat   = "0"

            if "." in x:
                integer, fraction = x.split(".")
                if "(" in fraction:
                    fraction, repeat = fraction.split("(")
                    repeat = repeat[ : -1] # remove ")"

            full = integer + "." + fraction + repeat * 20
            return float(full)

        return abs(parse(s) - parse(t)) < 0.0000000000000000001
17 lines | 570 bytes | submitted June 4, 2024 | GitHub   7% comment ratio | tab size 4 | low complexity

Difficulty

This problem is ranked as
easy medium hard

and has an acceptance rate of about 46% (20k+ submissions).

Typical patterns to solve this problem:
  • Math
  • String
I solved 663 easy, 1250 medium and 309 hard problems

AI Disclaimer

Every single line of code was written by me, 100% from scratch, between August 2023 and August 2024.
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.

See this dedicated page for more details.
LeetCode profile | GitHub repo

Code Walkthrough

Overview

The provided implementation defines a class Solution with a single public method isRationalEqual. The method receives two string representations of rational numbers, s and t, and determines whether they denote the same value. The strings may contain a plain integer, a terminating decimal, or a decimal with a repeating portion enclosed in parentheses. The core of the algorithm is a helper function parse that expands the representation into a finite decimal string, converts it to a floating-point number, and then the two results are compared using a very small tolerance.

Parsing Logic

The parse function is responsible for turning a textual rational number into a concrete floating-point value. It follows a step-by-step decomposition:

def parse(x):
    integer  = x
    fraction = "0"
    repeat   = "0"

    if "." in x:
        integer, fraction = x.split(".")
        if "(" in fraction:
            fraction, repeat = fraction.split("(")
            repeat = repeat[ : -1] # remove ")"

    full = integer + "." + fraction + repeat * 20
    return float(full)

The function starts by assuming the whole input is an integer (integer = x) and sets default placeholders for the non-repeating and repeating fractional parts. When a decimal point is present, the string is split into the integer part and the part after the point. If a left parenthesis appears in the fractional segment, the code further splits it to isolate the non-repeating fraction and the repeating block, stripping the trailing right parenthesis.

The crucial step is the construction of full, which concatenates the integer part, a decimal point, the non-repeating fraction, and the repeating block duplicated twenty times. This creates a finite decimal approximation that is sufficient for the subsequent floating-point conversion.

Comparison Logic

After both inputs have been parsed, the method evaluates their equality by measuring the absolute difference and checking whether it falls below a predefined epsilon:

return abs(parse(s) - parse(t)) < 0.0000000000000000001

The epsilon value is extremely small, effectively demanding that the two floating-point numbers be indistinguishable at the precision offered by the repeated expansion.

Edge Cases Handled

The parsing routine gracefully deals with several variations of input format. When the string lacks a decimal point, the initial assignment integer = x remains unchanged and the fractional placeholders stay at their default "0" values, resulting in a representation like "123.0" after the full construction. If a decimal point exists but there is no repeating portion, the inner if "(" in fraction block is bypassed, leaving repeat as "0"; the repeated block therefore contributes only zeros to the final string. When a repeating block is present but the non-repeating fraction is empty (e.g., "0.(3)"), the split operation yields an empty string for fraction, and the repeated segment is still appended twenty times, correctly forming "0.33333...". The removal of the closing parenthesis via slicing (repeat = repeat[ : -1]) ensures that only the digits inside the parentheses are used for repetition.

Performance Optimizations

The algorithm avoids any iterative processing of the repeating segment beyond a fixed multiplication. By repeating the captured block exactly twenty times, it eliminates the need for a loop that would otherwise depend on the length of the repeating pattern or the desired precision. This constant-time string multiplication is highly efficient in Python, leveraging internal optimizations for repeated concatenation. Converting the assembled string to a float is performed once per input, and the subsequent arithmetic comparison is a single subtraction and absolute value operation. The overall flow consists of a small, predictable number of string splits, a slice, a multiplication, and a float conversion, all of which execute quickly even for relatively long repeating sections.

written by openai gpt

ELI5 - Simplified Explanation

Imagine two friends want to know if their numbers are the same, even if one friend wrote the number nicely and the other friend wrote it with a long, repeating pattern.

The code works like a clever machine that looks at strings of text, which are just numbers written as characters. Inside the machine, there is a tiny worker that breaks each number apart into pieces. It finds the whole number at the front, then the part after the dot, and finally the part that keeps repeating forever.

Because the computer cannot actually count to infinity, the worker creates a trick. It takes the repeating part and pastes it together twenty times in a row. This creates a very long number that is close enough to infinity for the computer to understand.

Once the machine has turned both numbers into plain, simple decimal numbers, it compares them. Because computers get a little dizzy with very tiny amounts, the code checks if the difference between the two numbers is smaller than a teeny-tiny speck. If the gap between them is almost nothing, the machine decides that the two numbers are the same.

written by google gemini

Code Quality

Several LLMs ranked my code quality on a scale from 1 (very poor) to 10 (outstanding):
n = 27 | median = 4 | μ = 4.33 | σ = 1.91

Complexity Analysis

not analyzed yet

Other Solutions

I wrote a basic crawler to find other solutions to that problem.
Obviously, I did that after I solved the problem myself.

In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.

And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.

(click on a column header to sort the table)
author
author solutions
www AlgoMonster C++ Java Python
WalkCCC C++ Java Python
leetcode.ca C++ Java Python
LeetcodeHelp C++ Java Python
Progiez C++ Java Python
github black-shadows C++
charles-wangkai Java
everthis JS
JoshCrozier JS
suxrobGM C#
wisdompeak C++
AlgoMonster C++, Python, Java
WalkCCC C++, Python, Java
leetcode.ca C++, Java, Python
LeetcodeHelp C++, Java, Python
Progiez C++, Java, Python
black-shadows C++, C++
charles-wangkai Java
everthis JavaScript
JoshCrozier JavaScript
suxrobGM C#
wisdompeak C++
21 links | 11 authors | 5 languages

Solved On The Same Day

5 solutions   June 4, 2024

Extra

There are a few extra pages giving you some insights into my setup:
  • Tech Stack - my development environment and tools
  • AI - how AI helped me create this website (NOT solving the problems)
  • Progress - progression over time
  • Quality - code quality