재귀는 많은 알고리즘 문제에서 강력한 도구로 활용된다. 특히 문자열 처리 문제에서 재귀를 사용하면 간결하고 효율적인 해결책을 제시할 수 있다. 이번 포스트에서는 백준의 문제 25501번: 재귀의 귀재를 통해 재귀 함수를 이용한 팰린드롬 판별과 재귀 호출 횟수 세는 방법을 알아보겠다.
문제 : https://www.acmicpc.net/problem/25501
문제 설명
백준 온라인 저지의 25501번: 재귀의 귀재 문제는 주어진 문자열이 팰린드롬인지 여부를 판단하고, 이를 판별하는 과정에서 재귀 함수가 몇 번 호출되는지를 세는 문제이다. 팰린드롬이란, 앞에서부터 읽었을 때와 뒤에서부터 읽었을 때가 같은 문자열을 말한다. 예를 들어, “ABBA"나 “ABABA"는 팰린드롬이지만, “ABCA"는 팰린드롬이 아니다.
문제는 다음과 같은 방식으로 진행된다:
- 문자열 S가 주어진다.
- 재귀 함수를 사용하여 S가 팰린드롬인지 판별한다.
- 재귀 함수가 호출된 횟수를 세어 출력한다.
입력으로는 테스트케이스의 수 T와 그에 따른 T개의 문자열 S가 주어지며, 각 문자열에 대해 팰린드롬 여부와 재귀 호출 횟수를 출력해야 한다.
접근 방식
이 문제를 해결하기 위해 재귀 함수를 사용하여 문자열의 양 끝 문자부터 비교해 나가는 방식을 채택하였다. 재귀 함수는 문자열의 좌측 인덱스 l과 우측 인덱스 r을 받아 다음과 같이 동작한다:
- 기본 사례(Base Case):
- 만약 l이 r 이상이 되면, 모든 문자가 일치했으므로 팰린드롬이다.
- 재귀 단계(Recursive Step):
- S[l]과 S[r]이 다르면, 더 이상 확인할 필요 없이 팰린드롬이 아니다.
- S[l]과 S[r]이 같다면, l을 증가시키고 r을 감소시켜 다음 문자들을 비교하기 위해 재귀 호출을 한다.
또한, 재귀 호출의 횟수를 세기 위해 전역 변수나 참조 변수를 활용하여 호출될 때마다 카운트를 증가시킨다.
이 접근 방식은 문자열의 길이에 비례하는 시간 복잡도 O(N)을 가지며, N은 문자열의 길이이다. 이는 문자열의 절반만을 확인하면 되기 때문에 효율적이다.
C++ 코드와 설명
|
|
코드의 동작 단계별 설명
입력 및 초기화:
ios::sync_with_stdio(false);
와cin.tie(NULL);
을 통해 입출력 속도를 최적화한다.- 테스트케이스의 수 T를 입력받는다.
테스트케이스 처리:
- 각 테스트케이스마다 문자열 S를 입력받는다.
- 재귀 호출 횟수를 세기 위해
cnt
를 0으로 초기화한다. isPalindrome
함수를 호출하여 팰린드롬 여부를 판단하고, 그 결과와 재귀 호출 횟수를 출력한다.
재귀 함수
recursion
:- 함수가 호출될 때마다
cnt
를 1 증가시킨다. - 현재 좌측 인덱스 l이 우측 인덱스 r보다 크거나 같으면 모든 문자가 일치한 것이므로 1을 반환한다.
- S[l]과 S[r]이 다르면 팰린드롬이 아니므로 0을 반환한다.
- S[l]과 S[r]이 같으면, l을 증가시키고 r을 감소시켜 다음 문자들을 비교하기 위해 재귀 호출을 한다.
- 함수가 호출될 때마다
C++ without library 코드와 설명
|
|
코드의 동작 단계별 설명
입력 및 초기화:
scanf
를 사용하여 테스트케이스의 수 T를 입력받는다.
테스트케이스 처리:
- 각 테스트케이스마다 문자열 S를 입력받는다. 문자열의 최대 길이가 1000이므로
char S[1001];
로 선언한다. - 재귀 호출 횟수를 세기 위해
cnt
를 0으로 초기화한다. isPalindrome
함수를 호출하여 팰린드롬 여부를 판단하고, 그 결과와 재귀 호출 횟수를printf
를 통해 출력한다.
- 각 테스트케이스마다 문자열 S를 입력받는다. 문자열의 최대 길이가 1000이므로
재귀 함수
recursion
:- 함수가 호출될 때마다
cnt
를 1 증가시킨다. - 현재 좌측 인덱스 l이 우측 인덱스 r보다 크거나 같으면 모든 문자가 일치한 것이므로 1을 반환한다.
- S[l]과 S[r]이 다르면 팰린드롬이 아니므로 0을 반환한다.
- S[l]과 S[r]이 같으면, l을 증가시키고 r을 감소시켜 다음 문자들을 비교하기 위해 재귀 호출을 한다.
- 함수가 호출될 때마다
Python 코드와 설명
|
|
코드의 동작 단계별 설명
입력 및 초기화:
sys.setrecursionlimit(10000)
을 통해 재귀 한도를 늘려 긴 문자열도 처리할 수 있도록 한다.- 표준 입력을 읽어 모든 데이터를 한 번에 받아 처리한다.
테스트케이스 처리:
- 첫 번째 입력 값 T를 테스트케이스의 수로 설정한다.
- 각 테스트케이스마다 문자열 S를 입력받고, 재귀 호출 횟수를 세기 위해
cnt
를 0으로 초기화한다. isPalindrome
함수를 호출하여 팰린드롬 여부를 판단하고, 그 결과와 재귀 호출 횟수를 출력한다.
재귀 함수
recursion
:- 함수가 호출될 때마다
cnt
를 1 증가시킨다. - 현재 좌측 인덱스 l이 우측 인덱스 r보다 크거나 같으면 모든 문자가 일치한 것이므로 1을 반환한다.
- S[l]과 S[r]이 다르면 팰린드롬이 아니므로 0을 반환한다.
- S[l]과 S[r]이 같으면, l을 증가시키고 r을 감소시켜 다음 문자들을 비교하기 위해 재귀 호출을 한다.
- 함수가 호출될 때마다
결론
이번 문제를 통해 재귀 함수를 사용하여 문자열이 팰린드롬인지 여부를 판단하고, 재귀 호출의 횟수를 효율적으로 세는 방법을 배웠다. 재귀는 코드의 가독성을 높이고, 복잡한 문제를 단순하게 해결할 수 있는 강력한 도구이다. 하지만 재귀 호출의 깊이가 깊어질 경우 스택 오버플로우가 발생할 수 있으므로, 필요한 경우 재귀 한도를 조정하거나 반복문으로 대체하는 방안도 고려해야 한다. 또한, 재귀 호출 횟수를 세는 방식은 전역 변수를 사용하거나 함수의 인자로 참조 변수를 전달하는 방법으로 구현할 수 있으며, 이는 문제의 요구사항에 따라 적절히 선택해야 한다. 이번 문제를 통해 재귀의 활용과 효율적인 구현 방법에 대해 깊이 이해할 수 있었다.