규도자 개발 블로그
[프로그래머스/Level2/파이썬3(python3)] 괄호 변환 본문
[프로그래머스/Level2/파이썬3(python3)] 괄호 변환
문제
카카오에 신입 개발자로 입사한 "콘"은 선배 개발자로부터 개발역량 강화를 위해 다른 개발자가 작성한 소스 코드를 분석하여 문제점을 발견하고 수정하라는 업무 과제를 받았습니다. 소스를 컴파일하여 로그를 보니 대부분 소스 코드 내 작성된 괄호가 개수는 맞지만 짝이 맞지 않은 형태로 작성되어 오류가 나는 것을 알게 되었습니다. 수정해야 할 소스 파일이 너무 많아서 고민하던 "콘"은 소스 코드에 작성된 모든 괄호를 뽑아서 올바른 순서대로 배치된 괄호 문자열을 알려주는 프로그램을 다음과 같이 개발하려고 합니다.
용어의 정의
'(' 와 ')' 로만 이루어진 문자열이 있을 경우, '(' 의 개수와 ')' 의 개수가 같다면 이를 균형잡힌 괄호 문자열이라고 부릅니다.
그리고 여기에 '('와 ')'의 괄호의 짝도 모두 맞을 경우에는 이를 올바른 괄호 문자열이라고 부릅니다.
예를 들어, "(()))("
와 같은 문자열은 "균형잡힌 괄호 문자열" 이지만 "올바른 괄호 문자열"은 아닙니다.
반면에 "(())()"
와 같은 문자열은 "균형잡힌 괄호 문자열" 이면서 동시에 "올바른 괄호 문자열" 입니다.
'(' 와 ')' 로만 이루어진 문자열 w가 "균형잡힌 괄호 문자열" 이라면 다음과 같은 과정을 통해 "올바른 괄호 문자열"로 변환할 수 있습니다.
1. 입력이 빈 문자열인 경우, 빈 문자열을 반환합니다.
2. 문자열 w를 두 "균형잡힌 괄호 문자열" u, v로 분리합니다. 단, u는 "균형잡힌 괄호 문자열"로 더 이상 분리할 수 없어야 하며, v는 빈 문자열이 될 수 있습니다.
3. 문자열 u가 "올바른 괄호 문자열" 이라면 문자열 v에 대해 1단계부터 다시 수행합니다.
3-1. 수행한 결과 문자열을 u에 이어 붙인 후 반환합니다.
4. 문자열 u가 "올바른 괄호 문자열"이 아니라면 아래 과정을 수행합니다.
4-1. 빈 문자열에 첫 번째 문자로 '('를 붙입니다.
4-2. 문자열 v에 대해 1단계부터 재귀적으로 수행한 결과 문자열을 이어 붙입니다.
4-3. ')'를 다시 붙입니다.
4-4. u의 첫 번째와 마지막 문자를 제거하고, 나머지 문자열의 괄호 방향을 뒤집어서 뒤에 붙입니다.
4-5. 생성된 문자열을 반환합니다.
"균형잡힌 괄호 문자열" p가 매개변수로 주어질 때, 주어진 알고리즘을 수행해 "올바른 괄호 문자열"로 변환한 결과를 return 하도록 solution 함수를 완성해 주세요.
매개변수 설명
- p는 '(' 와 ')' 로만 이루어진 문자열이며 길이는 2 이상 1,000 이하인 짝수입니다.
- 문자열 p를 이루는 '(' 와 ')' 의 개수는 항상 같습니다.
- 만약 p가 이미 "올바른 괄호 문자열"이라면 그대로 return 하면 됩니다.
입출력 예
p | result |
---|---|
"(()())()" | "(()())()" |
")(" | "()" |
"()))((()" | "()(())()" |
입출력 예에 대한 설명
입출력 예 #1 이미 "올바른 괄호 문자열" 입니다.
입출력 예 #2
두 문자열 u, v로 분리합니다.
- u =
")("
- v =
""
- u =
u가 "올바른 괄호 문자열"이 아니므로 다음과 같이 새로운 문자열을 만듭니다.
- v에 대해 1단계부터 재귀적으로 수행하면 빈 문자열이 반환됩니다.
- u의 앞뒤 문자를 제거하고, 나머지 문자의 괄호 방향을 뒤집으면
""
이 됩니다. - 따라서 생성되는 문자열은
"("
+""
+")"
+""
이며, 최종적으로"()"
로 변환됩니다.
입출력 예 #3
두 문자열 u, v로 분리합니다.
- u =
"()"
- v =
"))((()"
- u =
문자열 u가 "올바른 괄호 문자열"이므로 그대로 두고, v에 대해 재귀적으로 수행합니다.
다시 두 문자열 u, v로 분리합니다.
- u =
"))(("
- v =
"()"
- u =
u가 "올바른 괄호 문자열"이 아니므로 다음과 같이 새로운 문자열을 만듭니다.
- v에 대해 1단계부터 재귀적으로 수행하면
"()"
이 반환됩니다. - u의 앞뒤 문자를 제거하고, 나머지 문자의 괄호 방향을 뒤집으면
"()"
이 됩니다. - 따라서 생성되는 문자열은
"("
+"()"
+")"
+"()"
이며, 최종적으로"(())()"
를 반환합니다.
- v에 대해 1단계부터 재귀적으로 수행하면
처음에 그대로 둔 문자열에 반환된 문자열을 이어 붙이면
"()"
+"(())()"
="()(())()"
가 됩니다.
풀이
def solution(p):
counter = 0
inner_counter = 0
if not p:
return ''
u = ''
v = ''
for current_p in p:
if current_p == '(':
counter += 1
u += current_p
else:
counter -= 1
u += current_p
if counter == 0:
v = p[len(u):]
u_is_right = True
for inner_p in u:
if inner_p == '(':
inner_counter += 1
else:
inner_counter -= 1
if inner_counter < 0:
u_is_right = False
if u_is_right:
return u + solution(v)
else:
new_u = ''
for i in u[1:-1]:
if i == '(':
new_u += ')'
else:
new_u += '('
answer = '(' + solution(v) + ')' + new_u
return answer
설명
2020 KAKAO BLINE RECRUITMENT 때 나왔던 문제. 이날 유독 투데이수가 뛰었길래 뭐지? 했더니 해당 컨테스트 참가자들이 이 문제를 검색해서 비슷한 문제를 통해 내 블로그 접속했던 기록이 많이 떴던게 기억이 난다. 바로 이 게시물이었다. 얼마 전 본 책에서 저 링크의 예제를 풀어준 게 있는데 특정 변수에 여는 괄호"("가 나오면 +1을 하고 닫는 괄호")"가 나오면 -1을 하는데 반복문이 종료됐을 때 그 변수가 0이면 올바른 괄호 문자열이고, 코드를 진행하는 도중 변수가 -1이 되면 닫히는 게 열린 것보다 더 나왔다는 얘기이니 반복문을 빠져나와서 해당 문자열이 '올바른 괄호 문자열'이 아님을 밝히면 되고, 반복문을 종료했을 때 0을 초과하면 '올바른 괄호 문자열'이 아니라고 풀이 한 것을 봤는데 그걸 보고 또 역시나 감탄이 나왔다. 난 복잡하게 풀었었는데...ㅠㅠ 아무튼 이 문제와는 연관 없으니 각설하고,
일단 문제를 풀기 전에 문자열의 두가지 형태에 대해서 염두할 필요가 있었다. 예제에서 볼 수 있다시피 '균형잡힌 괄호 문자열'과 '올바른 괄호 문자열'. 과거 문제처럼 틀린 괄호 문자열은 없다. 사실상 입출력 예 3번을 그대로 프로그램으로 구현하면 된다. 그래서 그냥 구현했다.
프로그래머스에서 제공하는 첫 재귀문제였다. 원래 변태적인 재귀함수를 좋아하는 지라 풀면서 굉장히 재미있었다. 물론 재귀함수는 공간복잡도로서도, 코드를 읽는 다른 사람에게로서도 별로 좋은 형태는 아니라고 생각하는 편이지만 역시 취향이라는 걸 간과할 수는 없더라. 뭔가 좀만 재귀가 들어갈 낌새가 보이면 재귀를 넣어볼까 하는 생각이 드는 걸 보면... 아무튼 재미있게 풀었다. 코드는 그냥 진짜 입출력 예#3의 서술을 그대로 코드로 옮겨놓으면 된다고밖에 설명을 못하겠다. 그리고 구현에 대한 부분도 다른 사람들은 함수를 많이 분리해놨던데 나는 개인적인 취향으로는 알고리즘 문제는 한 함수에 끝내고 싶다는 생각이 있어서 그냥 다 때려박았다.
'알고리즘 > 풀이' 카테고리의 다른 글
[프로그래머스/Level2/파이썬3(python3)] 오픈채팅방 (0) | 2021.03.31 |
---|---|
[프로그래머스/Level2/파이썬3(python3)] 숫자의 표현 (0) | 2021.03.30 |
[프로그래머스/Level2/파이썬3(python3)] 전화번호 목록 (0) | 2021.03.28 |
[프로그래머스/Level2/파이썬3(python3)] 행렬의 곱셈 (0) | 2021.03.28 |
[프로그래머스/Level2/파이썬3(python3)] 피보나치 수 (0) | 2021.03.22 |