← Back to list

[CT스터디] 코딩테스트 문제 풀이 기록: 헷갈린 지점을 끝까지 분해하며 해결한 4문제

From Confusion to Correctness: Debugging Recursive State, Index Logic, and Clock Simulation

software engineer · 2026-06-26 11:06 · 0 claps · 23.2 min read
#algorithmpractice #backtracking #simulation-time #python-algorithms #string-validation
Open on Medium ↗
Wiki topics: 💻 · Programming

[CT스터디] 코딩테스트 문제 풀이 기록: 헷갈린 지점을 끝까지 분해하며 해결한 4문제

From Confusion to Correctness: Debugging Recursive State, Index Logic, and Clock Simulation

1. 짧은 도입부

이번 기록은 Programmers 코딩테스트 문제 4개를 풀이하면서, 단순히 정답 코드를 만드는 것이 아니라 내가 실제로 헷갈렸던 지점을 하나씩 분해해서 이해한 과정을 정리한 글이다. 풀이한 문제는 사라지는 발판, 예상 대진표, 영어 끝말잇기, 아날로그 시계이다.

네 문제는 겉으로 보면 서로 다른 유형이다. 하나는 게임 상태 탐색이고, 하나는 토너먼트 번호 갱신 문제이며, 하나는 문자열 규칙 검증 문제이고, 마지막 하나는 시계 바늘의 각도와 시간을 다루는 시뮬레이션 문제다. 하지만 풀이 과정에서 공통적으로 마주친 문제는 같았다. 문제의 조건을 코드로 바로 옮기기 전에, 어떤 값을 상태로 보고 어떤 순간을 검사해야 하는지 먼저 정의해야 했다.

2. 핵심 작업 요약

이번 실습의 핵심 작업은 네 가지로 정리할 수 있다.

첫째, 사라지는 발판 문제에서는 재귀와 백트래킹을 사용해 게임 상태를 탐색했다. 현재 플레이어의 위치, 상대 플레이어의 위치, 사라진 발판 상태를 함께 고려해야 했다. 특히 현재 발판을 없앤 뒤 다시 복구하는 이유가 처음에는 직관적으로 이해되지 않았지만, 여러 선택지를 독립적으로 비교하려면 상태 복구가 필수라는 점을 확인했다.

둘째, 예상 대진표 문제에서는 토너먼트 전체를 만들지 않고 참가자 번호가 다음 라운드에서 어떻게 바뀌는지만 추적했다. 핵심 규칙은 (현재 번호 + 1) // 2였다. 이 수식을 통해 두 참가자의 번호가 같아지는 순간을 찾으면, 두 사람이 만나는 라운드를 알 수 있었다.

셋째, 영어 끝말잇기 문제에서는 중복 단어 검사와 끝말 연결 검사를 분리했다. 첫 단어를 왜 used_words에 저장해야 하는지, 탈락 조건에서 왜 and가 아니라 or를 써야 하는지, 사람 번호와 차례를 왜 index % n + 1, index // n + 1로 계산하는지를 단계적으로 정리했다.

넷째, 아날로그 시계 문제에서는 시간을 초 단위로 바꾸고, 각 시각에서 초침·분침·시침의 각도를 계산했다. 이후 1초 구간마다 초침이 분침 또는 시침을 뒤에서 따라잡아 앞지르는지 확인했다. 이 문제에서는 각도, % 360, current_second, next_second, start_time 처리 위치처럼 헷갈리는 개념이 많았고, 이를 하나씩 분해해 해결했다.

3. 문제 인식

처음에는 네 문제 모두 “조건을 읽고 구현하면 되는 문제”처럼 보였다. 하지만 직접 코드를 작성하려고 하자, 단순 구현보다 더 중요한 문제가 있었다. 바로 문제에서 말하는 상태가 코드에서 어떤 변수로 표현되어야 하는지가 명확하지 않으면, 코드가 길지 않아도 쉽게 헷갈린다는 점이었다.

예를 들어 영어 끝말잇기에서는 단어가 순서대로 주어지기 때문에 단순히 앞뒤 글자만 비교하면 될 것 같았다. 하지만 실제로는 이미 등장한 단어를 저장해야 하고, 탈락자가 발생했을 때 그 단어를 말한 사람 번호와 차례를 계산해야 했다. 여기서 index는 단순 배열 위치가 아니라, 사람 번호와 차례를 계산하는 기준이 된다.

아날로그 시계 문제에서는 더 큰 혼동이 있었다. 처음에는 “초침과 분침이 겹친다”는 말을 어떻게 코드로 표현해야 할지 막혔다. 사람은 시계를 보고 바늘이 겹쳤다는 것을 직관적으로 알 수 있지만, 컴퓨터는 위치를 숫자로 비교해야 한다. 그래서 시계 위치를 각도로 바꾸고, 12시 방향을 0도, 3시 방향을 90도, 6시 방향을 180도, 9시 방향을 270도로 정의해야 했다.

또한 current_secondnext_second라는 변수 이름도 헷갈릴 수 있었다. 이 값은 “현재 초”나 “다음 초”가 아니라, 현재 시각의 초침 각도1초 뒤의 초침 각도를 의미한다. 이 차이를 이해하지 못하면, 왜 60이 아니라 360으로 보정하는지 이해할 수 없다.

4. 문제 정의

이번 실습에서 각 문제는 다음과 같이 다시 정의할 수 있었다.

사라지는 발판은 “현재 플레이어가 이길 수 있는 상태인지 판단하고, 이긴다면 최소 이동 수, 진다면 최대 이동 수를 선택하는 문제”로 정의했다. 즉, 단순히 가능한 경로를 모두 탐색하는 문제가 아니라, 승패 여부와 이동 횟수를 함께 반환해야 하는 문제였다.

예상 대진표는 “두 참가자의 번호가 라운드마다 어떻게 압축되는지 추적하는 문제”로 정의했다. 전체 대진표를 직접 만들 필요는 없었다. ab가 같은 번호가 될 때까지 (번호 + 1) // 2를 반복하면 된다.

영어 끝말잇기는 “단어 배열을 앞에서부터 보며 처음으로 규칙을 어긴 인덱스를 찾는 문제”로 정의했다. 규칙 위반은 두 가지였다. 첫째, 이미 나온 단어를 다시 말하는 경우. 둘째, 이전 단어의 마지막 글자와 현재 단어의 첫 글자가 다른 경우. 둘 중 하나만 발생해도 탈락이다.

아날로그 시계는 “초침이 시침 또는 분침을 따라잡는 순간을 세는 문제”로 정의했다. 중요한 점은 세 바늘 모든 조합을 비교하는 것이 아니라, 알람 조건의 주인공인 초침만 기준으로 보면 된다는 점이었다. 따라서 비교 대상은 초침 vs 분침, 초침 vs 시침 두 개로 줄어든다.

5. 왜 이것을 문제로 인식했는가

이번 실습에서 가장 큰 문제는 “코드는 짧은데 왜 이해가 안 되는가”였다. 예를 들어 예상 대진표의 핵심 코드는 매우 짧다.

a = (a + 1) // 2
b = (b + 1) // 2

하지만 이 코드가 왜 다음 라운드 번호를 의미하는지 이해하지 못하면, 그냥 외운 코드가 된다. 1번과 2번 경기의 승자가 다음 라운드 1번, 3번과 4번 경기의 승자가 다음 라운드 2번이 되는 규칙을 먼저 이해해야 했다.

영어 끝말잇기에서도 비슷했다. person = index % n + 1, turn = index // n + 1은 공식처럼 보이지만, 사실은 단어 배열의 인덱스를 사람 번호와 차례로 바꾸는 코드다. 사람 번호는 1, 2, 3, 1, 2, 3...처럼 반복되므로 %를 사용하고, 차례는 n개 단어마다 한 번씩 증가하므로 //를 사용한다.

아날로그 시계에서는 훨씬 더 많은 개념이 한 번에 나왔다. 초침은 60초에 한 바퀴, 분침은 3600초에 한 바퀴, 시침은 43200초에 한 바퀴 돈다. 그래서 각 바늘의 위치를 초 단위 시간에서 각도로 바꿔야 한다. 하지만 계산 결과가 360도를 넘으면 다시 0도로 돌아오기 때문에 % 360이 필요하다. 이때 % 360은 나눗셈이 아니라, 한 바퀴를 돌고 남은 현재 위치를 구하는 연산이라는 점도 따로 이해해야 했다.

또한 is_alarm_at(start_time)을 반복문 안에 넣었다가 답이 이상해지는 문제도 있었다. 이 문제는 시작 시각 자체를 세는 처리와, 1초 구간 안에서 새로 발생하는 알람을 세는 처리를 분리하지 않아서 생겼다. 시작 시각이 이미 알람 상태라면 한 번만 세야 하는데, 반복문 안에 넣으면 매초마다 같은 시작 시각을 다시 검사하게 된다. 이 문제를 통해 코드의 위치가 로직의 의미를 바꾼다는 것을 확인했다.

6. 문제 해결 경험 1: 사라지는 발판 — 상태를 바꾸고 다시 복구해야 하는 이유

사라지는 발판 문제는 두 명의 플레이어가 번갈아 움직이고, 이동한 뒤 이전 발판이 사라지는 게임이다. 처음에는 가능한 방향으로 이동하며 재귀 탐색을 하면 된다고 생각할 수 있다. 하지만 실제로는 각 플레이어가 최적의 선택을 한다는 조건이 있기 때문에, 단순 탐색이 아니라 승패 판단을 함께 해야 한다.

처음 헷갈렸던 부분은 현재 발판을 0으로 만들었다가 다시 1로 복구하는 코드였다. 실제 게임에서는 발판이 사라지는데, 왜 다시 복구해야 하는지 직관적으로 이해하기 어려웠다. 원인은 재귀 탐색이 실제 게임 한 판만 진행하는 것이 아니라, 가능한 모든 선택지를 실험하기 때문이다. 한 방향으로 이동해 본 뒤에는 다른 방향도 같은 조건에서 비교해야 한다. 따라서 이전 선택이 다음 선택에 영향을 주지 않도록 원래 상태로 되돌려야 한다.

def solution(board, aloc, bloc):
    row = len(board)
    col = len(board[0])
    direction = [[1, 0], [0, 1], [-1, 0], [0, -1]]
    def can_go(r, c):
        if r < 0 or r >= row or c < 0 or c >= col:
            return False
        if board[r][c] == 0:
            return False
        return True
    def play(cur_r, cur_c, other_r, other_c):
        # 현재 사람이 서 있는 발판이 이미 사라졌으면 패배
        if board[cur_r][cur_c] == 0:
            return False, 0
        can_current_win = False
        # 내가 이길 수 있는 경우들 중 가장 짧은 이동 횟수
        min_win_count = float("inf")
        # 내가 질 수밖에 없는 경우들 중 가장 긴 이동 횟수
        max_lose_count = 0
        for dr, dc in direction:
            next_r = cur_r + dr
            next_c = cur_c + dc
            if not can_go(next_r, next_c):
                continue
            # 현재 사람이 이동하므로 현재 발판 제거
            board[cur_r][cur_c] = 0
            # 다음 차례는 상대
            other_can_win, move_count = play(
                other_r,
                other_c,
                next_r,
                next_c
            )
            # 다른 경우도 봐야 하므로 복구
            board[cur_r][cur_c] = 1
            total_move_count = move_count + 1
            # 상대가 지는 상태면 현재 사람은 이길 수 있음
            if not other_can_win:
                can_current_win = True
                min_win_count = min(min_win_count, total_move_count)
            else:
                max_lose_count = max(max_lose_count, total_move_count)
        if can_current_win:
            return True, min_win_count
        return False, max_lose_count
    can_a_win, answer = play(aloc[0], aloc[1], bloc[0], bloc[1])
    return answer

이 문제에서 복잡했던 개념은 “이길 수 있으면 빨리 이기고, 질 수밖에 없으면 오래 버틴다”는 조건이었다. 이 조건 때문에 이기는 경우에는 min_win_count를 선택하고, 지는 경우에는 max_lose_count를 선택해야 했다. 단순히 최단거리나 최장거리만 보는 문제가 아니라, 승패 상태에 따라 이동 횟수 선택 기준이 달라지는 문제였다.

이미지 1 — 사라지는 발판 정답 검증 화면

이미지 1 — 사라지는 발판 정답 검증 화면

검증 결과, 여러 테스트에서 기대한 이동 횟수가 반환되었고, 이미지 1에서 정답 처리 결과를 확인할 수 있었다.

7. 문제 해결 경험 2: 예상 대진표 — 전체 대진표를 만들지 않고 번호만 갱신하기

예상 대진표 문제는 토너먼트에서 두 참가자 a, b가 몇 번째 라운드에서 만나는지 구하는 문제다. 처음에는 전체 대진표를 만들어야 할 것처럼 보일 수 있다. 하지만 실제로 필요한 정보는 두 참가자의 번호가 다음 라운드에서 어떻게 바뀌는지뿐이었다.

핵심 규칙은 다음과 같다.

next_number = (current_number + 1) // 2

이 식은 홀수와 짝수 번호를 한 경기 묶음으로 압축한다. 예를 들어 1번과 2번은 다음 라운드에서 1번, 3번과 4번은 다음 라운드에서 2번, 5번과 6번은 다음 라운드에서 3번이 된다.

def solution(n, a, b):
    round_count = 0
    # a와 b가 같은 번호가 될 때까지 라운드 진행
    while a != b:
        a = (a + 1) // 2
        b = (b + 1) // 2
        round_count += 1
    return round_count

이 문제에서 헷갈렸던 부분은 “둘이 만난다”는 상황을 어떻게 코드로 표현하느냐였다. 처음에는 ab가 바로 붙어 있는지 확인해야 할 것처럼 느껴질 수 있다. 하지만 다음 라운드 번호로 갱신했을 때 두 번호가 같아진다는 것은, 이전 라운드에서 서로 경기했다는 뜻이다. 따라서 while a != b를 반복하고, 같아지는 순간의 라운드 수를 반환하면 된다.

이미지 2 — 예상 대진표 정답 검증 화면

이미지 2 — 예상 대진표 정답 검증 화면

이미지 2에서 예상 대진표 문제의 정답 검증 결과를 확인할 수 있었다.

8. 문제 해결 경험 3: 영어 끝말잇기 — 첫 단어 저장, or 조건, 인덱스 계산

영어 끝말잇기 문제는 사람들이 순서대로 단어를 말할 때, 가장 먼저 탈락하는 사람의 번호와 차례를 구하는 문제다. 이 문제에서 헷갈렸던 첫 번째 지점은 첫 단어 처리였다.

첫 단어는 앞 단어가 없으므로 끝말 연결 검사를 하지 않는다. 하지만 이미 게임에서 사용된 단어이므로 반드시 저장해야 한다. 그래야 나중에 같은 단어가 다시 나왔을 때 중복으로 잡을 수 있다.

두 번째 지점은 탈락 조건이었다. 탈락 조건은 두 가지다. 이미 나온 단어를 말했거나, 이전 단어의 마지막 글자와 현재 단어의 첫 글자가 다르면 탈락이다. 즉, 둘 중 하나만 틀려도 탈락이므로 and가 아니라 or를 사용해야 한다.

세 번째 지점은 사람 번호와 차례 계산이었다. 현재 단어의 인덱스가 index일 때, 사람 번호는 index % n + 1, 차례는 index // n + 1로 구한다.

def solution(n, words):
    used_words = set()
    used_words.add(words[0])
    for index in range(1, len(words)):
        previous_word = words[index - 1]
        current_word = words[index]
        is_duplicate = current_word in used_words
        is_wrong_chain = previous_word[-1] != current_word[0]
        if is_duplicate or is_wrong_chain:
            person = index % n + 1
            turn = index // n + 1
            return [person, turn]
        used_words.add(current_word)
    return [0, 0]

테스트 코드도 함께 작성했다.

# 테스트 1
n = 3
words = ["tank", "kick", "know", "wheel", "land", "dream", "mother", "robot", "tank"]
print(solution(n, words))  # [3, 3]
# 테스트 2
n = 5
words = [
    "hello", "observe", "effect", "take", "either",
    "recognize", "encourage", "ensure", "establish", "hang",
    "gather", "refer", "reference", "estimate", "executive"
]
print(solution(n, words))  # [0, 0]
# 테스트 3
n = 2
words = ["hello", "one", "even", "never", "now", "world", "draw"]
print(solution(n, words))  # [1, 3]

이 문제의 해결 과정에서 가장 중요했던 점은 공식처럼 보이는 코드를 표로 이해한 것이다. 예를 들어 n = 3이면 사람 번호는 1, 2, 3, 1, 2, 3...으로 반복된다. 이 반복 구조를 만들기 위해 % n을 사용하고, 파이썬 인덱스가 0부터 시작하므로 +1을 붙인다. 차례는 n개 단어마다 1씩 증가하므로 // n + 1을 사용한다.

이미지 3 — 영어 끝말잇기 정답 검증 화면

이미지 3 — 영어 끝말잇기 정답 검증 화면

이미지 3에서 영어 끝말잇기 문제의 정답 검증 결과를 확인할 수 있었다.

9. 복잡한 문제 해결 경험: 아날로그 시계 — 각도, 1초 구간, 시작 시각 처리

아날로그 시계 문제는 이번 실습에서 가장 많이 헷갈렸던 문제였다. 처음에는 왜 각도를 써야 하는지부터 막혔다. “바늘이 겹친다”는 말은 사람이 보기에는 자연스럽지만, 코드에서는 위치를 숫자로 바꿔야 비교할 수 있다. 그래서 시계판을 360도 원으로 보고, 각 바늘의 위치를 각도로 표현했다.

초침은 60초에 한 바퀴를 돈다. 한 바퀴는 360도이므로 초침은 1초에 6도 움직인다. 분침은 3600초에 360도 움직이므로 1초에 0.1도 움직인다. 시침은 43200초에 360도 움직이므로 1초에 1/120도 움직인다.

def get_angles(time):
    # 한 바퀴는 360도
    # 초침은 1초에 6도 움직임
    second_angle = (time * 6) % 360
    # 분침은 1초에 0.1도 움직임
    minute_angle = (time * 0.1) % 360
    # 시침은 1초에 1/120도 움직임
    hour_angle = (time * (1 / 120)) % 360
    return second_angle, minute_angle, hour_angle

여기서 % 360이 특히 헷갈렸다. % 360은 360으로 나누는 것이 아니라, 한 바퀴를 돌고 남은 현재 위치를 구하는 것이다. 예를 들어 초침이 366도 움직였다면, 실제 시계판에서는 한 바퀴 360도를 돌고 6도 위치에 있다. 그래서 366 % 360 = 6이 된다.

다음으로 헷갈렸던 지점은 current_second, next_second였다. 이 값들은 실제 초가 아니라 초침의 각도다. 그래서 59초에서 60초로 넘어갈 때 354도 → 0도처럼 보이지만, 실제 움직임은 354도 → 360도까지 앞으로 간 것이다. 비교가 깨지지 않도록 next_second == 0이면 360으로 보정했다.

또 하나의 핵심은 “초침이 가장 빠르다”는 점이다. 초침이 분침이나 시침보다 뒤에 있다가 1초 뒤에 같거나 앞에 있으면, 그 사이에 한 번 겹친 것이다. 이 생각으로 다음 조건을 만들었다.

meet_minute = current_second < current_minute and next_second >= next_minute
meet_hour = current_second < current_hour and next_second >= next_hour

마지막으로 헷갈렸던 부분은 is_alarm_at(start_time)의 위치였다. 시작 시각에 이미 초침이 분침 또는 시침과 겹쳐 있으면 알람을 1번 세야 한다. 하지만 이 검사를 반복문 안에 넣으면, 매초마다 같은 시작 시각을 다시 검사하게 되어 답이 이상해진다. 그래서 반복문 밖에서 딱 한 번만 실행해야 했다.

아래는 최종 풀이 코드다.

def get_angles(time):
    second_angle = (time * 6) % 360
    minute_angle = (time * 0.1) % 360
    hour_angle = (time * (1 / 120)) % 360
    return second_angle, minute_angle, hour_angle
def is_alarm_at(time):
    second_angle, minute_angle, hour_angle = get_angles(time)
    return second_angle == minute_angle or second_angle == hour_angle
def solution(h1, m1, s1, h2, m2, s2):
    answer = 0
    # 시작 시간과 끝 시간을 초 단위로 변환
    start_time = h1 * 3600 + m1 * 60 + s1
    end_time = h2 * 3600 + m2 * 60 + s2
    # 시작 시각에 이미 겹쳐 있으면 딱 1번만 센다.
    if is_alarm_at(start_time):
        answer += 1
    # 1초 구간마다 알람 발생 여부 확인
    for time in range(start_time, end_time):
        current_second, current_minute, current_hour = get_angles(time)
        next_second, next_minute, next_hour = get_angles(time + 1)
        # 0도로 돌아가는 순간은 비교할 때 360도로 본다.
        if next_second == 0:
            next_second = 360
        if next_minute == 0:
            next_minute = 360
        if next_hour == 0:
            next_hour = 360
        # 초침이 분침을 뒤에서 따라잡았는지 확인
        meet_minute = current_second < current_minute and next_second >= next_minute
        # 초침이 시침을 뒤에서 따라잡았는지 확인
        meet_hour = current_second < current_hour and next_second >= next_hour
        if meet_minute:
            answer += 1
        if meet_hour:
            answer += 1
        # 12시 정각에는 세 바늘이 동시에 겹친다.
        # 위에서 +2로 세었을 수 있으므로 1번 빼서 +1로 만든다.
        if meet_minute and meet_hour and time + 1 == 43200:
            answer -= 1
    return answer

테스트 코드도 함께 작성했다.

print(solution(0, 5, 30, 0, 7, 0))        # 2
print(solution(12, 0, 0, 12, 0, 30))      # 1
print(solution(0, 6, 1, 0, 6, 6))         # 0
print(solution(11, 59, 30, 12, 0, 0))     # 1
print(solution(11, 58, 59, 11, 59, 0))    # 1
print(solution(1, 5, 5, 1, 5, 6))         # 2
print(solution(0, 0, 0, 23, 59, 59))      # 2852

이미지 4 — 아날로그 시계 정답 검증 화면

이미지 4 — 아날로그 시계 정답 검증 화면

이미지 4에서 아날로그 시계 문제의 정답 검증 결과를 확인할 수 있었다.

10. 성과

이번 실습의 성과는 단순히 네 문제를 모두 정답 처리했다는 점에만 있지 않다. 더 중요한 성과는, 각 문제에서 내가 헷갈렸던 지점을 명확히 언어화하고, 그 지점이 코드에서 어떤 변수와 조건으로 해결되는지 연결했다는 점이다.

사라지는 발판에서는 상태 변경과 복구를 이해했다. 예상 대진표에서는 번호 갱신 규칙을 수식으로 압축했다. 영어 끝말잇기에서는 중복 검사, 연결 검사, 인덱스 기반 사람 번호 계산을 분리했다. 아날로그 시계에서는 시간, 각도, 1초 구간, 시작 시각 보정을 각각 별도의 문제로 나누어 해결했다.

특히 아날로그 시계 문제에서는 한 번에 모든 조건을 이해하려고 하지 않고, 시간을 초로 바꾸기 → 바늘 위치를 각도로 바꾸기 → 현재와 1초 뒤를 비교하기 → 0도 보정하기 → 시작 시각 처리하기 순서로 문제를 나누었다. 이 방식은 복잡한 문제를 작은 단위로 분해하는 연습이 되었다.

11. 사용한 주요 수식/코드 정리

사라지는 발판: 재귀 상태 복구

board[cur_r][cur_c] = 0
other_can_win, move_count = play(other_r, other_c, next_r, next_c)
board[cur_r][cur_c] = 1

이 코드는 하나의 이동 선택지를 실험한 뒤, 다른 선택지도 같은 조건에서 비교하기 위해 상태를 복구하는 코드다. 상태 복구가 없으면 이전 선택의 결과가 다음 탐색에 영향을 주므로 정답이 깨진다.

예상 대진표: 다음 라운드 번호

a = (a + 1) // 2
b = (b + 1) // 2

이 수식은 현재 라운드 번호를 다음 라운드 번호로 압축한다. 두 번호가 같아지는 순간이 두 참가자가 만난 라운드다.

영어 끝말잇기: 탈락 조건과 위치 계산

is_duplicate = current_word in used_words
is_wrong_chain = previous_word[-1] != current_word[0]
if is_duplicate or is_wrong_chain:
    person = index % n + 1
    turn = index // n + 1
    return [person, turn]

이 코드는 중복 단어 또는 끝말 연결 실패를 감지하고, 해당 단어를 말한 사람 번호와 차례를 계산한다.

아날로그 시계: 각도 계산

second_angle = (time * 6) % 360
minute_angle = (time * 0.1) % 360
hour_angle = (time * (1 / 120)) % 360

이 수식은 시계 바늘 위치를 각도로 변환한다. % 360은 한 바퀴를 돌고 남은 현재 위치를 구하기 위해 사용한다.

아날로그 시계: 1초 구간에서 겹침 감지

meet_minute = current_second < current_minute and next_second >= next_minute
meet_hour = current_second < current_hour and next_second >= next_hour

이 조건은 현재는 초침이 뒤에 있고, 1초 뒤에는 같거나 앞에 있는지를 확인한다. 초침이 가장 빠르기 때문에 이 조건으로 1초 사이에 겹친 순간을 감지할 수 있다.

12. 최종 정리

이번 실습은 코딩테스트 문제를 푸는 과정에서 “헷갈리는 지점”을 그냥 넘기지 않고 끝까지 분해한 기록이다. 단순히 정답 코드를 받아들이는 것이 아니라, 왜 그 코드가 그 위치에 있어야 하는지, 왜 그 연산자를 써야 하는지, 왜 그 변수를 비교해야 하는지를 확인했다.

특히 아날로그 시계 문제에서 is_alarm_at(start_time)을 반복문 밖에 두어야 하는 이유를 이해한 것은 중요한 디버깅 경험이었다. 시작 시각 자체를 세는 로직과, 1초 구간 안에서 새로 발생하는 알람을 세는 로직은 역할이 다르다. 이 둘을 분리하지 않으면 답이 누적되어 이상해진다.

이처럼 문제를 해결하는 과정은 단순 구현이 아니라, 상태와 조건의 책임을 분리하는 과정이었다. 이 기록은 이후 더 복잡한 알고리즘 문제를 풀 때도 같은 방식으로 적용할 수 있는 기반이 된다.

13. Portfolio Summary

This coding test practice focused on transforming ambiguous problem statements into explicit state models and verifiable implementation logic. Across four problems, I practiced recursive game-state search, tournament state compression, string rule validation, and time-angle simulation. The main outcome was not only passing the tests, but also identifying why each piece of code was necessary and where specific logic should be placed.

The most valuable part of this practice was debugging conceptual confusion. I clarified why recursive state must be restored after each branch, why tournament numbers can be updated with a simple integer formula, why word-chain validation requires both duplication and connection checks, and why clock-hand overlap can be detected by comparing current and next angular positions. This process strengthened my ability to decompose complex algorithmic problems into smaller, testable rules.

14. Key skills practiced

  • Recursive game-state modeling
  • Backtracking and state restoration
  • Win/loss state propagation
  • Minimax-like decision logic
  • Integer state transition modeling
  • Index-based turn calculation
  • Set-based duplicate detection
  • String boundary comparison
  • Time-to-seconds conversion
  • Angle-based simulation
  • Modular arithmetic with % 360
  • Edge-case handling at 0/360 degrees
  • Start-state validation
  • Test-case based verification
  • Debugging by isolating responsibility of code blocks

메타데이터
post_id
17eea5c87682
slug
ct스터디-코딩테스트-문제-풀이-기록-헷갈린-지점을-끝까지-분해하며-해결한-4문제-17eea5c87682
url
https://medium.com/@jmin54492/ct%EC%8A%A4%ED%84%B0%EB%94%94-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EA%B8%B0%EB%A1%9D-%ED%97%B7%EA%B0%88%EB%A6%B0-%EC%A7%80%EC%A0%90%EC%9D%84-%EB%81%9D%EA%B9%8C%EC%A7%80-%EB%B6%84%ED%95%B4%ED%95%98%EB%A9%B0-%ED%95%B4%EA%B2%B0%ED%95%9C-4%EB%AC%B8%EC%A0%9C-17eea5c87682
canonical_url
https://medium.com/@jmin54492/ct%EC%8A%A4%ED%84%B0%EB%94%94-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EA%B8%B0%EB%A1%9D-%ED%97%B7%EA%B0%88%EB%A6%B0-%EC%A7%80%EC%A0%90%EC%9D%84-%EB%81%9D%EA%B9%8C%EC%A7%80-%EB%B6%84%ED%95%B4%ED%95%98%EB%A9%B0-%ED%95%B4%EA%B2%B0%ED%95%9C-4%EB%AC%B8%EC%A0%9C-17eea5c87682
author_url
https://medium.com/@jmin54492
status
ok
fetched_at
2026-07-13 13:12:13