ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [python] 문자열 뒤집기의 모든 것
    Programming/Python 2026. 3. 22. 18:57
    반응형

    한번 생성하면 수정할 수 없다.

    파이썬에서 문자열을 다룰 때 반드시 기억해야 할 대전제가 있습니다. 

    바로 "파이썬의 문자열은 한 번 만들어지면 수정할 수 없다(Immutable)"는 것입니다.

     

    따라서 문자열의 일부분을 뒤집든 전체를 뒤집든, 파이썬은 기존 문자열을 조작하는 것이 아니라
    "뒤집힌 새로운 문자열을 메모리에 새로 만들어내는 과정"을 거칩니다.

    이 원리를 이해하면 어떤 뒤집기 방식을 선택해야 할지 명확해집니다. 

     

     


    1. 슬라이싱 (Slicing) : 파이썬 생태계의 절대 강자

    전체 문자열이든 부분 문자열이든, 파이썬에서 문자열을 뒤집을 때 가장 먼저 고려해야 할 최고의 방법입니다.

    text = "Hello, World!"
    
    # 전체 문자열 뒤집기
    reversed_text = text[::-1]
    
    # 부분 문자열 뒤집기 (인덱스 1부터 4까지인 'ello'를 뒤집음)
    # 주의: 역순 슬라이싱은 시작점과 끝점의 위치도 반대로 적어야 합니다.
    sub_reversed = text[4:0:-1]

     

    내부 동작 원리

    파이썬의 슬라이싱은 파이썬 언어 레벨이 아니라, 파이썬을 구동하는 핵심 엔진인 C언어 레벨에서 처리됩니다. 

     

    [::-1] 명령을 만나면 내부적으로 기존 문자열과 동일한 크기의 메모리를 단번에 새로 할당받고,

    C언어의 포인터를 이용해 문자열의 끝에서부터 처음으로 빠르게 역진입 하며 글자를 복사해 넣습니다.

     

    시간복잡도 : O(N)

    공간복잡도 : O(N) (새로운 문자열 생성)

     

    💡평가

    코드가 극단적으로 짧고 직관적이며, C언어 단에서 최적화 되어 실행 속도가 파이썬에서 구현할 수 있는 가장 빠른 속도를 자랑합니다. 부분 문자열을 뒤집을 때도 인덱스만 조절하면 되므로 가독성이 매우 뛰어납니다.

     


    2. reversed() 와 join() : 메모리 효율과 객체 지향적 접근

    문자열뿐만 아니라 리스트, 튜플 등 다양한 데이터를 뒤집을 때 사용하는 파이썬의 내장 함수 활용법입니다.

    text = "Python"
    
    # reversed()는 뒤집힌 '상태'를 내뱉는 이터레이터를 반환합니다.
    # 이를 "",join()을 통해 하나의 문자열로 다시 조립합니다.
    
    reversed_text = "".join(reversed(text))

     

    내부 동작 원리

    reversed(text)는 호출 즉시 새로운 문자열을 만들지 않습니다.

    대신, 원본 문자열의 맨 끝을 가리키는 '이터레이터' 객체만 덜렁 하나 생성합니다.

     

    이후 .join() 함수가 이터레이터에게 다음 글자를 요청할 때마다 뒤에서부터 한 글자씩 던져주며, 이를 모아 최종적으로 하나의 새로운 문자열을 완성합니다. 

     

    시간복잡도 : O(N)

    공간복잡도 : O(N) (결과 문자열 조립용)

     

    💡평가

    슬라이싱 [::-1]보다 함수 호출이라는 오버헤드가 발생하며 미세하게 느립니다. 하지만 문자열을 '리스트'나 '튜플'처럼 다른 순회 가능한 객체와 동일한 논리로 취급하고 싶을 때(객체 지향적 다형성), 의미를 명확하게 전달하기 좋은 코드입니다.

    1. 순회 가능한 객체란?
    파이썬에서 for문을 돌려 안에 있는 요소를 하나씩 차례대로 꺼낼 수 있는 데이터 타입들을 통들어 "순회 가능한 객체(iterable)"라고 부릅니다.
      - 문자열 : 글자를 하나씩 꺼낼 수 있음 ('a, 'p', 'p', 'l', 'e')
      - 리스트 : 요소를 하나씩 꺼낼 수 있음 ([1, 2, 3])
      - 튜플 : 요소를 하나씩 꺼낼 수 있음 ((4, 5, 6))
    즉, 파이썬 입장에서는 문자열이든, 리스트든, 튜플이든 근본적으로 "값을 하나씩 꺼내줄 수 있는 주머니"라는 점에서 완전히 동일한 취급을 받습니다.

    2. 코드로 보는 '동일한 논리'의 의미
    만약 어떤 데이터가 들어오든 뒤집어서 출력해 주는 범용적인 함수를 만들고 싶다고 가정해 보겠습니다. 
    def print_backward(data):
    	for item in reversed(data):
        	print(item, end=" ")
        print()
        
    #1. 문자열을 넣었을 때
    print_backward("Hello") # 출력 : o l l e H
    
    #2. 리스트를 넣었을 때
    print_backward([1,2,3]) # 출력 : 3 2 1
    
    #3. 튜플을 넣었을 때
    print_backward((10, 20 ,30)) # 출력 : 30 20 10

     

      - 슬라이싱의 한계 : 슬라이싱은 시퀀스(순서가 있는) 타입에만 쓸 수 있는 특수한 문법입니다. 만약 내가 직접 만든 커스텀 객체나 다른 형태의 데이터 구조라면 에러가 날 수 있습니다.
      - reversed()의 범용성 : 반면 reversed()라는 내장 함수는 "문자열이든 리스트든 상관없이, 그냥 하나씩 데이터를 꺼낼 수 있는 iterable이면, 무조건 뒤에서 꺼냅니다" 라는 논리로 작동합니다.

    💡핵심

    "문자열을 리스트나 튜플처럼 다른 순회 가능한 객체와 동일한 논리로 취급하고 싶을 때"라는 말은, 문자열만을 위한 특수한 문법(슬라이싱 등)을 쓰지 않고, 파이썬의 모든 반복 가능한 데이터에 공통적으로 먹히는 표준적인 방법(reversed())을 사용하여 일관성 있는 코드를 작성하고 싶을 때를 뜻합니다.

     


    3. 투 포인터 (Two Pointers) : 조건부 뒤집기의 핵심 알고리즘

    전체 문자열 중 "특정 조건을 만족하는 문자만 위치를 바꾸고 싶을 때" 사용하는 알고리즘 기법입니다.

    text = "Python"
    
    # 1.문자열은 수정이 불가능하므로, 수정 가능한 리스트로 변환합니다. 
    arr = list(text)
    
    # 2.양 끝에 포인터를 배치합니다. 
    left = 0
    right = len(arr) - 1
    
    # 3. 두 포인터가 만날 때까지 위치를 교환하며 안쪽으로 좁혀옵니다.
    while left < right :
    	# 특정 조건을 걸어 제어할 수 있습니다. 
        arr[left], arr[right] = arr[right], arr[left]
        left += 1
        right += 1
        
    # 4. 리스트를 다시 문자열로 합칩니다. 
    reversed_text = "".join(arr)

     

    내부 동작 원리

    C언어나 자바에서는 문자열 배열 안에서 직접 교환 (O(1) 공간)이 가능하지만, 파이썬은 불변이므로 무조건 list()로 한 번 변환하여 메모리에 펼쳐놓은 뒤 조작해야 합니다.

     

    시간복잡도 : O(N)

    공간복잡도 : O(N) (리스트 변환 메모리 필요)

     

    💡평가

    단순히 전체를 뒤집을 때는 코드가 길어지고 속도도 슬라이싱보다 느려 비효율적입니다. 하지만 문제의 조건이 복잡해질수록 코드의 확장성 측면에서는 다른 방법들과 달리 높은 유연성을 보여줍니다.

     


    4. for 반복문 활용 : 절대 피해야 할 안티 패턴 

    다른 프로그래밍 언어를 쓰다가 파이썬을 사용할 때 자주하는 실수입니다.

    text = "Python"
    reversed_text = ""
    
    for char in text :
    	#기존 문자열 앞에 새로운 문자를 계속 이어 붙입니다. 
        reversed_text = char + reversed_text

     

    내부 동작 원리

    파이썬의 문자열은 불변이므로 연산을 할 때마다 기존 문자열에 글자를 추가하는 것이 아니라,

    두 문자열을 합친 크기만큼의 새로운 메모리를 매번 다시 할당받습니다. 즉, 반복문이 돌 때마다 쓸데없는 메모리 생성과 복사 작업이 기하급수적으로 발생합니다. 

     

    시간복잡도 : O(N^2)

    만약 이 글자가 10,000글자라면 어떨까요?
      - 1 + 2 + 3 + ... + 10000
      - 가우스의 합 공식을 빌려(n(n+1)/2 로 계산해보면, 무려 약 5,000만 번의 쓸데없는 메모리 복사 작업이 일어납니다.
    이것이 바로 반복문 안에서 문자열 + 연산을 남발하면 연산량이 O(N^2) 으로 폭발하는 '시간 초과'가 발생하는 원리입니다.

     

    공간복잡도 : O(N)

     

    💡평가

    학습용으로 문자열 조립 과정을 이해하기 좋지만, 실제 개발이나 코딩 테스트에서는 극악의 시간 복잡도를 유발합니다. 
    절대 사용해서는 안되는 코드입니다. 

     


    5. 최종 요약 비교표

    비교 항목 슬라이싱 reversed() 투 포인터 for 반복문
    핵심 기법 C 레벨 메모리 복사 이터레이터 지연 평가 양방향 배열 인덱스 스왑 문자열 재할당 및 병합
    시간 복잡도 O(N)  O(N)  O(N)  O(N^2) 
    공간 복잡도 O(N)  O(N)  O(N)  O(N) 
    코드의 유연성 단순 뒤집기에 완벽 이터러블 객체와 호환성 가장 유연함 낮음
    가독성 매우 좋음(파이썬스러움) 좋음(의미론적 명확성) 보통(코드가 김) 매우 안좋음

     

    반응형
Designed by Tistory.