Self Improvement
2025-04-02
6 min read

3일차 초콜릿.. 아니 문제 풀이

3일차 문제는 3가지 풀이로 즐긴다

오늘은 3일 차 문제를 풀어보자.

image.png

위 문제에서 입력으로는 수식 하나가 주어지고 그 수식의 계산 결과를 출력하는 문제이다.

수식은 기본적으로 하나의 숫자(0 또는 1)가 주어지고, 이 숫자에 팩토리얼 또는 논리 반전 연산을 적용할 수 있다.

그러면 입력은 아래와 같이 주어진다.

0! -> 0 팩토리얼은 1
1! -> 1 팩토리얼은 1
!0 -> 0의 논리 반전은 1
!1 -> 1의 논리 반전은 0
!!0!! -> 0의 팩토리얼은 1 의 팩토리얼은 1. 1에 논리 반전을 두 번 적용하면 1
!!1!! -> 1의 팩토리얼은 1 의 팩토리얼은 1. 1에 논리 반전을 두 번 적용하면 1

팩토리얼이니 논리 반전이니 말이 어렵지만 간단히 정리하면

숫자 오른 쪽 부분에 ! 가 한 개라도 나오면 그 부분은 1로 평가

되고,

숫자 왼 쪽 부분에 ! 가 나오면 그 횟수만큼(짝/홀) 논리를 반전시킨다.

1!!!!! 이던 0!!!!!! 이던 1이라는 얘기다. 그러면 나머지는 숫자 앞에 있는 ! 연산만 남게 된다.

로직 구상

  1. 수식 맨 끝에 ! 가 붙어있으면 논리 반전 연산자를 적용하기 이전까지의 피연산자는 1이 된다.

    그렇지 않으면 등장한 숫자가 피연산자가 된다.

  2. 숫자 앞에 있는 ! 갯수를 세어 짝수인지, 홀수인지에 따라 논리를 반전시킨다.

    짝수: 피연산자 그대로

    홀수: !피연산자

구현1

import sys

input = sys.stdin.readline

def eval(equation: str) -> int:
    exclamation_occured = 0
    if equation[-1] == '!': # 수식 뒤에 ! 가 붙어 있다면..
        exclamation_occured = 1
    
    operand = 0
    reversal_count = 0
    for ch in equation:
        if ch.isdigit(): # 숫자 등장
            if exclamation_occured: # 피연산자 1 로 간주
                operand = 1
            else:
                operand = int(ch)
            break
        reversal_count += 1
    
    if reversal_count % 2 == 0: # 숫자 앞 단에 붙어있는 느낌표 갯수가 짝수이면
        return operand
    else:
        return int(not operand) # 홀수이면 논리 반전

def solution():
    n = int(input())
    for _ in range(n):
        print(eval(input()[:-1]))

if __name__ == '__main__':
    solution()

이 코드는 단순하지만 몇 가지 개선할 만한 부분이 있다.

  1. exclamation_occured 라는 변수는 굳이 필요가 없다.

    그냥 피연산자(operand) 라고 하자.

…그냥 조금 깔끔하게 써보자. 읽기 쉽고 이해하기 쉽게.

구현22

import sys

input = sys.stdin.readline

def eval(equation: str) -> int:
    operand = int(equation[-1] == '!') # if 문은 불필요하다.

    right = len(equation) - 1
    while equation[right] == '!': # 숫자 찾기
        right -= 1

    operand = int(equation[right]) | operand # 느낌표가 있다(1)면 OR 연산으로 무조건 1처리

		# if right < 0.. 하려다가 max 함수로 lower-bound 를 0으로 처리
    reversal_count = max(right, 0)

		# 같으면 0, 다르면 1. 논리 반전 횟수가 짝수(0)이면 그대로, 홀수(1)이면 반전
    return operand ^ reversal_count % 2

def solution():
    n = int(input())
    for _ in range(n):
        print(eval(input()[:-1]))

if __name__ == '__main__':
    solution()

또 다르게도 풀어보았다.

구현333

import sys

input = sys.stdin.readline

def eval(equation: str) -> int:
    for i, char in enumerate(equation):
        if char in '01': # 숫자가 등장하면
            n = int(char)
            # 숫자를 기준으로 반갈
            prefix = equation[:i]
            suffix = equation[i+1:]
            break
    
    # 팩토리얼(!) 연산
    if len(suffix):
        n = 1  # 0!와 1! 모두 1이므로..
    
    return n ^ (len(prefix) % 2) # 이거는 2번째 풀이와 동일하다.

def solution():
    n = int(input())
    for _ in range(n):
        print(eval(input().rstrip('\n')))

if __name__ == '__main__':
    solution()

마지막

오늘은 문제를 여러 가지 방법으로 풀어보았다.

난이도는 그렇게 어렵지는 않았지만 문제를 여러 방법으로 해결해보면서 로직 상 불필요한 부분을 없애고 코드가 간결해졌다.

Joonseok Kim
Joonseok Kim
Software Engineer