|

문자열 뒤집기 최소 횟수, 왜 그룹 수의 최솟값이 답일까

문자열 뒤집기 최소 횟수 대표 이미지
문자 개수보다 연속 구간을 봐야 답이 쉬워지는 대표 문제

문자열 뒤집기 최소 횟수 문제는 처음 보면 그리디처럼 보입니다. 하지만 실제 핵심은 “지금 어디를 뒤집을까”가 아니라, 문자 하나가 아니라 연속된 그룹을 봐야 한다는 사실을 알아차리는 데 있습니다.

결론부터 말하면 답은 min(0 그룹 수, 1 그룹 수)입니다. 이번 글에서는 왜 이 식이 정확한지, 그리고 왜 이 문제가 그리디 느낌은 나지만 본질은 관찰형 애드혹 문제인지 차근차근 설명해보겠습니다.


문제 조건

0과 1로만 이루어진 문자열이 있습니다. 여기서 같은 숫자가 연속된 구간 하나를 골라, 그 구간 전체를 뒤집을 수 있습니다. 목표는 문자열 전체를 전부 0으로 만들거나, 전부 1로 만드는 것입니다. 이때 필요한 최소 뒤집기 횟수를 구하면 됩니다.

대표 예시는 0001100입니다. 이 문자열을 보면 많은 사람이 먼저 0의 개수와 1의 개수를 세지만, 이 문제는 그 방향으로 들어가면 설명이 자주 꼬입니다.


개수보다 그룹

이 문제의 연산은 문자 하나를 뒤집는 연산이 아닙니다. 연속된 덩어리 하나를 한 번에 뒤집는 연산입니다. 이 차이가 아주 중요합니다.

예를 들어 111은 1이 세 개지만, 이 문제에서 111은 세 번 처리하는 대상이 아닙니다. 한 번에 뒤집을 수 있는 1개의 그룹입니다.

  • 문자 개수보다 그룹 개수가 중요합니다.
  • 원소보다 덩어리가 중요합니다.
  • 최소 횟수는 그룹 수와 직접 연결됩니다.

이렇게 관점을 바꾸면 문제가 훨씬 단순해집니다.


핵심 관찰

문자열을 왼쪽부터 읽으면서 값이 바뀌는 지점을 보면, 문자열이 몇 개의 그룹으로 나뉘는지 알 수 있습니다. 예를 들어 0001100000, 11, 00으로 나뉩니다.

  • 0 그룹: 2개
  • 1 그룹: 1개
문자열을 스캔하며 0 그룹과 1 그룹을 세는 흐름도
왼쪽부터 훑으며 새 그룹이 시작될 때만 그룹 수를 올리면 된다

이제 바로 계산할 수 있습니다. 모두 0으로 만들고 싶다면 1 그룹들만 뒤집으면 되고, 모두 1로 만들고 싶다면 0 그룹들만 뒤집으면 됩니다. 따라서 정답은 자연스럽게 두 그룹 수 중 작은 값이 됩니다.


정답이 되는 이유

가능성

모두 0으로 만들고 싶다면 1 그룹을 전부 뒤집으면 됩니다. 각 1 그룹은 한 번의 연산으로 처리할 수 있으므로, 1 그룹 수만큼 연산하면 반드시 전체를 0으로 만들 수 있습니다. 모두 1로 만드는 경우도 완전히 같은 논리입니다.

최소성

서로 떨어져 있는 두 그룹을 한 번에 없앨 수 없기 때문입니다. 예를 들어 00110011의 1 그룹은 두 개입니다. 사이에 0이 끼어 있으므로, 이 둘을 한 번의 연산으로 동시에 처리할 수는 없습니다.

결국 모두 0으로 만들려면 각 1 그룹을 적어도 한 번씩은 처리해야 합니다. 그래서 모두 0으로 만드는 최소 횟수는 정확히 1 그룹 수입니다. 모두 1로 만드는 최소 횟수도 정확히 0 그룹 수입니다.

즉 이 값은 어림짐작이 아니라, 실제로 가능하면서 동시에 더 줄일 수 없는 정확한 최소값입니다.


예시 풀이

0001100

먼저 그룹으로 나누면 000 | 11 | 00입니다. 따라서 0 그룹은 2개, 1 그룹은 1개입니다.

전부 0으로 만들기
1단계: 000 [11] 00
뒤집은 뒤: 0000000
총 1번입니다.

전부 1로 만들기
1단계: [000] 11 001111100
2단계: 11111 [00]1111111
총 2번입니다.

따라서 정답은 min(2, 1) = 1입니다.

010101

그룹은 0 | 1 | 0 | 1 | 0 | 1입니다. 0 그룹 3개, 1 그룹 3개이므로 어느 쪽으로 맞춰도 3번이 필요합니다. 정답은 3입니다.

11111

그룹은 11111 하나뿐입니다. 0 그룹은 0개, 1 그룹은 1개입니다. 이미 모두 1이므로 정답은 min(0, 1) = 0입니다.

001100011

그룹은 00 | 11 | 000 | 11입니다. 0 그룹 2개, 1 그룹 2개이므로 전부 0으로 만들든 전부 1로 만들든 2번이 필요합니다. 정답은 2입니다.


그리디처럼 보이는 이유

많은 풀이가 이 문제를 그리디로 분류합니다. 완전히 틀렸다고 보기는 어렵습니다. 마지막에 두 값 중 작은 쪽을 고르기 때문입니다.

하지만 문제를 푸는 진짜 핵심은 현재 최선의 선택을 반복하는 것이 아닙니다. 먼저 전부 0으로 만들면 1 그룹 수만큼 필요하고, 전부 1로 만들면 0 그룹 수만큼 필요하다는 구조를 발견해야 합니다.

즉 핵심은 선택 전략보다 구조 관찰입니다. 그래서 이 문제는 그리디처럼 답을 고르는 순간이 있더라도, 본질적으로는 구간 관찰형 애드혹 문제라고 보는 편이 더 정확합니다.


구현 포인트

문자열을 왼쪽부터 보면서 직전 문자와 현재 문자가 다를 때마다 새 그룹이 시작됐다고 판단하면 됩니다.

  • 현재 문자가 0이면 zero_groups를 1 증가
  • 현재 문자가 1이면 one_groups를 1 증가
  • 마지막에 min(zero_groups, one_groups)를 반환

시간 복잡도는 O(N)이고, 추가 메모리는 거의 들지 않습니다.


Python 코드

def min_flip_count(s: str) -> int:
    zero_groups = 0
    one_groups = 0
    prev = ''

    for ch in s:
        if ch != prev:
            if ch == '0':
                zero_groups += 1
            else:
                one_groups += 1
            prev = ch

    return min(zero_groups, one_groups)


print(min_flip_count("0001100"))   # 1
print(min_flip_count("010101"))    # 3
print(min_flip_count("11111"))     # 0

이 코드가 세는 것은 문자 개수가 아니라 새로운 그룹이 시작된 횟수입니다. 이 해석만 정확하면 구현은 매우 단순합니다.


자주 하는 실수

개수 비교

가장 흔한 실수입니다. 이 문제는 몇 개가 많은지가 아니라, 몇 개의 덩어리로 나뉘어 있는지가 중요합니다.

전환 횟수

값이 바뀐 횟수는 힌트가 되지만, 그 자체가 바로 정답은 아닙니다. 결국 0 그룹이 몇 개이고 1 그룹이 몇 개인지까지 연결해야 합니다.

예외 처리

0000, 1111 같은 경우는 답이 0이어야 합니다. 그룹 수 관점으로 보면 이 예외는 따로 외울 필요도 없이 자동으로 처리됩니다.


이 문제에서 남는 감각

이 문제는 단순히 공식을 외우고 끝내기에는 아까운 문제입니다. 더 중요한 것은 이런 질문을 습관처럼 던지는 것입니다.

이 문제는 개수 문제인가, 아니면 구간 문제인가?

문자열, 배열, 압축, 색칠, 구간 병합 같은 문제에서는 원소 하나하나보다 연속된 덩어리를 보는 순간 훨씬 쉬워지는 경우가 많습니다. 이 문자열 뒤집기 문제는 그 감각을 익히기에 정말 좋은 대표 예시입니다.


정리

문자열 뒤집기 최소 횟수 문제의 답이 min(0 그룹 수, 1 그룹 수)인 이유는 분명합니다. 모두 0으로 만들려면 1 그룹들을 뒤집어야 하고, 모두 1로 만들려면 0 그룹들을 뒤집어야 하며, 떨어져 있는 그룹은 한 번에 같이 처리할 수 없기 때문입니다.

즉 이 문제는 겉으로는 그리디처럼 보여도, 실제로는 연속 구간을 읽어내는 관찰이 먼저인 문제입니다. 관련 글로는 애드혹 알고리즘 접근법: 그리디와 완전탐색 판단 기준, 애드혹 알고리즘 반례 찾는 법, 시뮬레이션 문제 풀이법: 상태 정의와 구현 순서를 함께 읽어보면 감각이 더 잘 이어집니다.

문제 원형은 백준 1439 뒤집기에서 직접 확인할 수 있습니다.

함께보면 좋은 글