From 5b551183732be10f392c2d987a77d241a42f5141 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 20 Oct 2023 10:38:20 -0400 Subject: [PATCH] More fun with roman numbers --- leetcode/pyproject.toml | 4 +-- leetcode/stuff.py | 65 ++++++++++++++++++++++++++++++++++++++++- leetcode/test_stuff.py | 12 ++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/leetcode/pyproject.toml b/leetcode/pyproject.toml index c8f6999..f19d787 100644 --- a/leetcode/pyproject.toml +++ b/leetcode/pyproject.toml @@ -78,12 +78,12 @@ all = [ [tool.black] target-version = ["py37"] -line-length = 120 +line-length = 90 skip-string-normalization = true [tool.ruff] target-version = "py37" -line-length = 120 +line-length = 90 select = [ "A", "ARG", diff --git a/leetcode/stuff.py b/leetcode/stuff.py index 0403906..8a04d5d 100644 --- a/leetcode/stuff.py +++ b/leetcode/stuff.py @@ -36,7 +36,9 @@ def gen_sqrt_check(n: int) -> typing.Callable[[float], int]: return check -def find_bisect(lower: float, upper: float, check: typing.Callable[[float], int]) -> float: +def find_bisect( + lower: float, upper: float, check: typing.Callable[[float], int] +) -> float: mid: float = lower + ((upper - lower) / 2) print(f"lower={lower} mid={mid} upper={upper}") @@ -72,3 +74,64 @@ def cartesian_path(p0: tuple[int, int], p1: tuple[int, int]) -> list[tuple[int, def gen_matrix(width: int, height: int) -> list[list[int]]: return [list(range(width)) for _ in range(height)] + + +class Roman: + SIMPLE = { + "I": 1, + "V": 5, + "X": 10, + "L": 50, + "C": 100, + "D": 500, + "M": 1000, + } + SIMPLE_REVERSE = {v: k for k, v in SIMPLE.items()} + COMPOUND = { + "IV": 4, + "IX": 9, + "XL": 40, + "XC": 90, + "CD": 400, + "CM": 900, + } + PREFIXES = {k[0] for k in COMPOUND.keys()} + COMPOUND_REVERSE = {v: k for k, v in COMPOUND.items()} + ALL = SIMPLE | COMPOUND + ALL_REVERSE = {v: k for k, v in ALL.items()} + + @classmethod + def i2r(cls, i: int) -> str: + r: list[str] = [] + + for int_val, roman_val in sorted(cls.ALL_REVERSE.items(), reverse=True): + remainder = i % int_val + + r += [roman_val] * int((i - remainder) / int_val) + + i = remainder + + return "".join(r) + + @classmethod + def r2i(cls, r: str) -> int: + total = 0 + offset = 0 + + for i in range(len(r)): + if i + offset > len(r) - 1: + break + + c = r[i + offset] + if ( + c in cls.PREFIXES + and (i + offset + 1) < len(r) + and c + r[i + offset + 1] in cls.ALL + ): + total += cls.ALL[c + r[i + offset + 1]] + offset += 1 + continue + + total += cls.ALL[c] + + return total diff --git a/leetcode/test_stuff.py b/leetcode/test_stuff.py index 1820bac..6238589 100644 --- a/leetcode/test_stuff.py +++ b/leetcode/test_stuff.py @@ -16,3 +16,15 @@ import stuff ) def test_find_sqrt_ish(n: int, expected: int): assert stuff.find_sqrt_ish(n) == expected + + +@pytest.mark.parametrize( + ("n", "expected"), + [ + (3, "III"), + (58, "LVIII"), + (1994, "MCMXCIV"), + ], +) +def test_int_to_roman(n: int, expected: str): + assert stuff.Roman.i2r(n) == expected