Created
October 12, 2025 14:54
-
-
Save hanjae-jea/4da387cbff9a089d36ab7a2e5a9c6aea to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def test_extract(): | |
res = extract('matt is the best school in korea') | |
assert res == 'MICLI', res | |
# 첫 번째 규칙: 보통 큰 숫자가 왼쪽 > 작은 숫자 오른쪽, | |
# 숫자의 값을 더한 값이 총합이다. | |
def test_passes_rule1(): | |
r1, _ = rule1('MICLI') | |
assert r1 == False | |
r2, s2 = rule1('MCI') | |
assert r2 == True and s2 == 1101, s2 | |
# 두 번째 규칙: 기호 V, L, D는 한 번만 사용할 수 있고, I,X,C는 **연속**해서 **세 번**까지만 쓸 수 있다. | |
# M은 몇 번이고 사용할 수 있다. | |
def test_passes_rule2(): | |
t1, _ = rule2('MLLV') | |
assert t1 == False | |
t2, _ = rule2('VV') | |
assert t2 == False | |
t2_1, _ = rule2('DLLI') | |
assert t2_1 == False | |
t3, _ = rule2('LXIIII') | |
assert t3 == False | |
t4, s4 = rule2('LXIII') | |
assert t4 == True and s4 == 50 + 10 + 3, s4 | |
# 세 번째 규칙 | |
# IV = 4, IX = 9, XL = 40, XC = 90, CD = 400, CM = 900 | |
# 각각 한 번씩 사용할 수 있다. 근데, (IV, IX) , (XL,XC), (CD, CM) 은 같이 나올 수 없다. | |
# 그리고 이들 외에는 작은 숫자가 큰 숫자 왼쪽 어디에도 나올 수 없다. | |
def test_rule3_convert(): | |
t1_s = rule3_convert('XCXC') | |
assert t1_s == 'EE', t1_s | |
t2 = rule3_convert('CMCD') | |
assert t2 == 'QW', t2 | |
t3 = rule3_convert('VX') | |
assert t3 == 'VX', t3 | |
t4 = rule3_convert('IIX') | |
assert t4 == 'IT' | |
t5 = rule3_convert('IXIV') | |
assert t5 == 'TY' | |
t6 = rule3_convert('XLCM') | |
assert t6 == 'RQ' | |
t6_1 = rule3_convert('CMXL') | |
assert t6_1 == 'QR' | |
t6_2 = rule3_convert('CMLXIV') | |
assert t6_2 == 'QLXY' | |
t7 = rule3_convert('MCMXL') | |
assert t7 == 'MQR' | |
def test_passes_rule3(): | |
# '아, rule1 재활용이 안되겠다.' | |
t1, _ = rule3('XCXC') | |
assert t1 == False | |
t2, _ = rule3('CMCD') | |
assert t2 == False, (t2, _) | |
t3, _ = rule3('VX') | |
assert t3 == False | |
t4, _ = rule3('IIX') | |
assert t4 == False | |
t5, _ = rule3('IXIV') | |
assert t5 == False | |
t6, _ = rule3('XLCM') | |
assert t6 == False | |
t6_1, s6_1 = rule3('CMXL') | |
assert t6_1 == True and s6_1 == 900 + 40, (t6_1, s6_1) | |
t6_2, s6_2 = rule3('CMLXIV') | |
assert t6_2 == True and s6_2 == 900 + 60 + 4 | |
t7, s7 = rule3('MCMXL') | |
assert t7 == True and s7 == 1940 | |
# 네 번째 규칙 | |
# 가장 적은 개수의 로마 숫자로 표현 해야 한다 | |
# 60 = LX , XLXX -> False, IVI -> False | |
def test_passes_rule4(): | |
t1, s1 = rule4('LX') | |
assert t1 == True and s1 == 60 | |
t2, s2 = rule4('XLXX') | |
assert t2 == False | |
t3, s3 = rule4('IVI') | |
assert t3 == False | |
# 여러 숫자도 표현할 수 있다고 볼 수 있으면 가장 큰 수를 출력해라 | |
'''rome_dic = { | |
'M': 1000, | |
'Q': 900, | |
'D': 500, | |
'W': 400, | |
'C': 100, | |
'E': 90, | |
'R': 40, | |
'L': 50, | |
'X': 10, | |
'T': 9, | |
'V': 5, | |
'Y': 4, | |
'I': 1 | |
}''' | |
# 첫 번째 처리: M은 그냥 많이 나오는게 좋다 | |
# MM(!@*#&!@#)M(!@*@!#)M(!@#) -> MMMM(!@#) | |
# CM -> M | |
# 일단 M을 뽑아서 M사이에 거치적 거리는걸 지운다 | |
def test_check_run1(): | |
t1 = run1('MIVM') | |
assert t1 == 'MM', t1 | |
t2 = run1('MIVMC') | |
assert t2 == 'MMC', t2 | |
t3 = run1('MCM') | |
assert t3 == 'MM', t3 | |
t4 = run1('MIVMCVII') | |
assert t4 == 'MMCVII', t4 | |
# 두 번째 처리: D가 나오면, | |
# (p1)D(p2)D(p3)D(p4) .. | |
# Dp2 / Dp3 / Dp4 중에 가장 큰 수가 답이다 | |
def test_check_run2(): | |
t1 = run2('CIID') | |
assert t1 == 'D', t1 | |
t2 = run2('CIIDXIIDCII') | |
assert t2 == 'DCII', t2 | |
t3 = run2('MMMCIIDXIIDCII') | |
assert t3 == 'MMMDCII', t3 | |
# 세 번째 처리: C에 대해서 | |
# (p1)C(p2)C(p3)C(p4)C(p5)C(p6)C(p7)C(p8)C(p9) | |
# CC? -> 절대 불가능 | |
# CD = 400? -> 절대 불가능 -> run2에서 CD는 안 보기로 했음 | |
# CCC(p4/p5/p6) | |
# 약간 M이랑 비슷해지는 것 같기도...최대 제약이 3회이고 나머지 중에서 최대를 골라야 한다 | |
# 3 번 뒤부터 나오는 것들 중에서 최대를 골라야 한다. 그 전이면 상관없이 C부터 골라야 한다 | |
# 미구현 상태 | |
def test_check_run3(): | |
t1 = run3('MMMDXXCXXCXXCXXCXXCXXCXXCXX') | |
assert t1 == 'MMMDCCCXX' | |
t2 = run3('MMMD(XX)-(CXLX)`(CLXX)`(CXXV)(CXXI)(CXXVII)(CXXVI)(CXXVI)') | |
assert t2 == 'MMMDCCCXXVII' | |
# L 은 D랑 똑같다. | |
# X는 C랑 똑같다. | |
# V는 D랑 똑같다. | |
# I는 똑같지만 뭐 없다. | |
rome_chars = 'IVXLCDM' | |
def extract(s: str) -> str: | |
res = '' | |
for c in s.upper(): | |
if c in rome_chars: | |
res += c | |
return res | |
rome_dic = { | |
'M': 1000, | |
'Q': 900, | |
'D': 500, | |
'W': 400, | |
'C': 100, | |
'E': 90, | |
'R': 40, | |
'L': 50, | |
'X': 10, | |
'T': 9, | |
'V': 5, | |
'Y': 4, | |
'I': 1 | |
} | |
def rule1(s: str) -> tuple[bool, int]: | |
sumation = 0 | |
for i in range(len(s)): | |
sumation += rome_dic[s[i]] | |
if i+1 < len(s) and rome_dic[s[i]] < rome_dic[s[i+1]]: | |
return False, 0 | |
return True, sumation | |
only_once = 'VLDQWERTY' | |
three_in_a_row = 'IXC' | |
def rule2(s: str) -> tuple[bool, int]: | |
rule1_result, sumation = rule1(s) | |
if rule1_result == False: | |
return rule1_result, sumation | |
for c in only_once: | |
if s.count(c) > 1: | |
return False, 0 | |
for c in three_in_a_row: | |
if s.count(c) > 3: # 최대 3번 까지 | |
return False, 0 | |
# 연속인지 아닌지 검증 -> rule1 을 통과하고 있다면, 안해도 되긴 해요 | |
# 나중에 rule1에 예외가 존재할 수 있다는 걸 안다면 | |
# 여기서 솔직히 **연속**도 점검을 해두는게 맞긴 하다. | |
if c * s.count(c) not in s: # X 가 두번 있다면 무조건 XX 형태로 존재해야 하니 | |
# c * s.count(c) -> X * 2 -> 'XX' -> s 안에 있어야 한다 | |
return False, 0 | |
# 최대 연속으로 3번 등장 | |
return rule1_result, sumation | |
rome_2char_dic = { | |
'CM': 'Q', # 900 | |
'CD': 'W', # 400 | |
'XC': 'E', # 90 | |
'XL': 'R', # 40 | |
'IX': 'T', # 9 | |
'IV': 'Y' # 4 | |
} | |
def rule3_convert(s: str) -> str: | |
for k, v in rome_2char_dic.items(): | |
s = s.replace(k, v) | |
return s | |
def rule3(s: str) -> tuple[bool, int]: | |
# rule1을 그대로 쓰면 안된다. | |
# 1. 치환을 잘 했는지 체크 (rule3_convert) | |
# 2. 규칙을 지켰는지 체크 | |
# (각각 한 번씩 사용할 수 있다.) 근데, (IV, IX) , (XL,XC), (CD, CM) 은 같이 나올 수 없다. | |
s = rule3_convert(s) | |
result, sumation = rule2(s) | |
if result == False: | |
return result, sumation | |
if s.count('Q') > 0 and s.count('W') > 0 or \ | |
s.count('E') > 0 and s.count('R') > 0 or \ | |
s.count('T') > 0 and s.count('Y') > 0: | |
return False, 0 | |
return result, sumation | |
def rule4(s): | |
return rule3(s) | |
# extract 되어있는 s | |
def run1(s: str) -> str: | |
# M 갯수 구하고, 마지막 M 인덱스 뒤에 있는 것들을 붙인다 | |
m_count = s.count('M') | |
if m_count > 0: | |
return ('M' * m_count) + s[s.rfind('M') + 1:] | |
else: | |
return s | |
# D 단위로 쪼개고, 물론 없으면 전체 return | |
# D로 시작하는 단위로 각각 쪼개고, 그 중에 최선을 선택 | |
# CD는요? -> 안써요 -> CD 보다는 D 가 더 큰수라서요 | |
def run2(s: str) -> str: | |
s = run1(s) | |
m_count = s.count('M') | |
s = s.replace('M', '') | |
idx = 0 | |
largest_substr_and_sumation: tuple[str, int] = '', 0 | |
while idx != -1: | |
pos = s.find('D', idx) | |
if pos == -1: | |
if idx == 0: | |
substr = s[idx:] | |
else: | |
substr = s[idx-1:] | |
idx = -1 | |
else: | |
if idx == 0: | |
substr = s[idx:pos] | |
else: | |
substr = s[idx-1:pos] | |
idx = pos + 1 | |
result, sumation = rule4(substr) | |
if result == True and largest_substr_and_sumation[1] < sumation: | |
largest_substr = substr, sumation | |
return f'{"M" * m_count}{largest_substr[0]}' | |
test_extract() | |
test_passes_rule1() | |
test_passes_rule2() | |
test_rule3_convert() | |
test_passes_rule3() | |
test_check_run1() | |
test_check_run2() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment