균형잡힌 세상
1 초 | 128 MB | 127782 | 43201 | 33559 | 32.668% |
문제
세계는 균형이 잘 잡혀있어야 한다. 양과 음, 빛과 어둠 그리고 왼쪽 괄호와 오른쪽 괄호처럼 말이다.
정민이의 임무는 어떤 문자열이 주어졌을 때, 괄호들의 균형이 잘 맞춰져 있는지 판단하는 프로그램을 짜는 것이다.
문자열에 포함되는 괄호는 소괄호("()") 와 대괄호("[]")로 2종류이고, 문자열이 균형을 이루는 조건은 아래와 같다.
- 모든 왼쪽 소괄호("(")는 오른쪽 소괄호(")")와만 짝을 이뤄야 한다.
- 모든 왼쪽 대괄호("[")는 오른쪽 대괄호("]")와만 짝을 이뤄야 한다.
- 모든 오른쪽 괄호들은 자신과 짝을 이룰 수 있는 왼쪽 괄호가 존재한다.
- 모든 괄호들의 짝은 1:1 매칭만 가능하다. 즉, 괄호 하나가 둘 이상의 괄호와 짝지어지지 않는다.
- 짝을 이루는 두 괄호가 있을 때, 그 사이에 있는 문자열도 균형이 잡혀야 한다.
정민이를 도와 문자열이 주어졌을 때 균형잡힌 문자열인지 아닌지를 판단해보자.
입력
각 문자열은 마지막 글자를 제외하고 영문 알파벳, 공백, 소괄호("( )"), 대괄호("[ ]")로 이루어져 있으며, 온점(".")으로 끝나고, 길이는 100글자보다 작거나 같다.
출력
각 줄마다 해당 문자열이 균형을 이루고 있으면 "yes"를, 아니면 "no"를 출력한다.
예제 입력 1 복사
So when I die (the [first] I will see in (heaven) is a score list).
[ first in ] ( first out ).
Half Moon tonight (At least it is better than no Moon at all].
A rope may form )( a trail in a maze.
Help( I[m being held prisoner in a fortune cookie factory)].
([ (([( [ ] ) ( ) (( ))] )) ]).
.
.
예제 출력 1 복사
yes
yes
no
no
no
yes
yes
힌트
7번째의 " ."와 같이 괄호가 하나도 없는 경우도 균형잡힌 문자열로 간주할 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char check(char* start, char* end) {
int check_small = 0;
int check_big = 0;
char* position_small = NULL;
char* position_big = NULL;
char* small_history[101] = { NULL };
char* big_history[101] = { NULL };
int small_history_index = 0;
int big_history_index = 0;
for (char* i = start; i <= end; i++) {
if (*i == '(') {
check_small++;
position_small = i;
small_history[small_history_index++] = position_small;
}
else if (*i == '[') {
check_big++;
position_big = i;
big_history[big_history_index++] = position_big;
}
else if (*i == ')') {
check_small--;
if (check_small < 0 || check_big < 0) {
return 0;
}
if (check(position_small + 1, i - 1) == 0)
return 0;
if (small_history_index >= 2) {
small_history_index--;
position_small = small_history[small_history_index-1];
}
else
position_small = NULL;
}
else if (*i == ']') {
check_big--;
if (check_small < 0 || check_big < 0) {
return 0;
}
if (check(position_big + 1, i - 1) == 0)
return 0;
if (big_history_index >= 2) {
big_history_index--;
position_big = big_history[big_history_index-1];
}
else
position_big = NULL;
}
}
if (check_small == 0 && check_big == 0) {
return 1;
}
else {
return 0;
}
}
char check_gpt(char* start, char* end) {
char stack[101]; // 스택으로 사용할 배열
int stackIndex = 0; // 스택의 현재 인덱스
for (char* i = start; i != end; i++) {
if (*i == '(' || *i == '[') {
// 여는 괄호를 스택에 추가
stack[stackIndex++] = *i;
}
else if (*i == ')' || *i == ']') {
// 스택이 비었거나, 짝이 맞지 않는 경우
if (stackIndex == 0 || (stack[stackIndex - 1] == '(' && *i != ')') || (stack[stackIndex - 1] == '[' && *i != ']')) {
return 0;
}
// 짝이 맞는 경우 스택에서 제거
stackIndex--;
}
}
// 스택이 비어있지 않다면 괄호의 짝이 맞지 않음
return stackIndex == 0 ? 1 : 0;
}
int main() {
char input[101] = { 0, };
char* end_of_string = NULL;
while (1) {
fgets(input, sizeof(input), stdin);
end_of_string = strrchr(input, '.');
if (end_of_string == NULL) //'.'을 안친 경우
continue;
if (end_of_string == input) // '.'만 친 경우
break;
else {
if (check_gpt(input, end_of_string))
printf("yes\n");
else
printf("no\n");
}
}
return 0;
}
본래 생각했던 풀이 방법은, '('와 '['의 위치를 계속 기록하고, 만약 괄호가 닫힌다면 괄호 사이에 있는 문자열을 인자로 재귀호출하여, 내부 문장이 적절한지 판단하는 방법이였다.
이전 괄호의 위치를 포인터 배열로 관리하며, 재귀호출로 인해 코드가 읽기 힘들고, 비효율적으로 동작하는 안좋은 코드가 만들어졌다.
반면 gpt가 작성한 코드는 stack을 이용해서 짝이 맞는 경우아 최종적으로 스택에 아무것도 남지 않을 경우 1을 리턴, 짝이 맞지 않는 경우 0을 리턴하는 코드를 작성하였다. 이는 훨씬 직관적이고 이해하기 쉬우며, 효율적이다.
함수를 직관적으로 작성하다 재귀함수를 사용해야 할 경우, 스택을 이용해볼 생각을 하는게 좋다고 한다.
하지만, 스택을 이용했을때 보기 더 힘들거나, 재귀 함수를 이용하므로써 tail recursion optimization을 받을 수 있는 경우엔 재귀 함수를 사용하는게 더 좋다고 한다.
자료구조를 배운 만큼, 사용해보려 노력해야겠다 ㅜ
'자료구조론 > 백준' 카테고리의 다른 글
[백준] 11866 요세푸스 문제 0 (0) | 2024.02.25 |
---|---|
[백준] 10866 덱 (0) | 2024.02.25 |
[백준] 2164 카드2 (1) | 2024.02.24 |
[백준] 1966 프린터 큐 (1) | 2024.02.24 |
[백준] 1874 스택 수열 (2) | 2024.02.18 |