본문 바로가기

백준 PS일지/Stack

[백준/C언어] 4949번 : 균형잡힌 세상 _코드리뷰+ 느낀점

와 진짜... 

이문제 풀면서 뭐가 문제인지 몰라서 엄청 막막해했었는데.,

문제 풀면서 scanf의 문제점과, 다른 .. C언어로 문자열 받는법 (3가지) 를 알게 되었고

 

문제 없는 코드의 문제 원인 찾고자 입력값도 바꿔보고, 

백준 컴파일에 맞춰서 c++17버전, vscode로 gcc 설치 등등..

제가 왜 이렇게틀렸나구요??.

 

문장을 입력받으면 yes or no로 출력되어야하는데

저는

YES or NO로 출력했다는 사실... 

(참뉔ㅋ..)

(아놔 억울해..)

결국 제 코드에 문제가 있었다는 사실..

이때 알았습니다. 백준은 표준적인 함수만 사용해야하고,

정답도 대소문자 가리는 .. 확실한 정답만을 추구한다는 것을요


https://www.acmicpc.net/problem/4949

 

4949번: 균형잡힌 세상

하나 또는 여러줄에 걸쳐서 문자열이 주어진다. 각 문자열은 영문 알파벳, 공백, 소괄호("( )") 대괄호("[ ]")등으로 이루어져 있으며, 길이는 100글자보다 작거나 같다. 입력의 종료조건으로 맨 마

www.acmicpc.net


문제

세계는 균형이 잘 잡혀있어야 한다. 양과 음, 빛과 어둠 그리고 왼쪽 괄호와 오른쪽 괄호처럼 말이다.

정민이의 임무는 어떤 문자열이 주어졌을 때, 괄호들의 균형이 잘 맞춰져 있는지 판단하는 프로그램을 짜는 것이다.

문자열에 포함되는 괄호는 소괄호("()") 와 대괄호("[]")로 2종류이고, 문자열이 균형을 이루는 조건은 아래와 같다.

  • 모든 왼쪽 소괄호("(")는 오른쪽 소괄호(")")와만 짝을 이뤄야 한다.
  • 모든 왼쪽 대괄호("[")는 오른쪽 대괄호("]")와만 짝을 이뤄야 한다.
  • 모든 오른쪽 괄호들은 자신과 짝을 이룰 수 있는 왼쪽 괄호가 존재한다.
  • 모든 괄호들의 짝은 1:1 매칭만 가능하다. 즉, 괄호 하나가 둘 이상의 괄호와 짝지어지지 않는다.
  • 짝을 이루는 두 괄호가 있을 때, 그 사이에 있는 문자열도 균형이 잡혀야 한다.

정민이를 도와 문자열이 주어졌을 때 균형잡힌 문자열인지 아닌지를 판단해보자.

입력

하나 또는 여러줄에 걸쳐서 문자열이 주어진다. 각 문자열은 영문 알파벳, 공백, 소괄호("( )") 대괄호("[ ]")등으로 이루어져 있으며, 길이는 100글자보다 작거나 같다.

입력의 종료조건으로 맨 마지막에 점 하나(".")가 들어온다.

출력

각 줄마다 해당 문자열이 균형을 이루고 있으면 "yes"를, 아니면 "no"를 출력한다.


문제는 이렇습니다.

최대 100 글자까지 , 입력 도중 '.' 입력시 문자열 판정 끝

문자열 => 안녕)(클래오)하하. 이 문장은 오류입니다. )가 먼저 나올순 없습니다.

괄호의 쌍은 오직(일때)[일때]로 1ㄷ1 대응되어야합니다.

공백 또한 문자열로 취급되며 공백 "  ." 이 문자열도 문자열로 취급됩니다.

언제 문제가 끝나냐?

입력받는 문자열의 첫번째 index 문자가 '.'일시 입력은 종료됩니다.

 

제가 이 문제를 풀며 고민했던 몇 가지가 있는데 그중 한가지

  • scanf("%s")는 공백이 있는 문자열을 받기에 적합하지 않다. 다른것을 써야한다.

2021.09.26 - [C언어] - [C언어] scanf()의 정의와 오류, 문자열 입력받는 3가지 방법scanf(),gets(),fgets()

(위 링크는 공백 포함 문자열 받는 방법이 나와있습니다.)

 

  • pop()함수를 선언했을때
pop(int top){
	--top;
}

초기에 이렇게 선언했었는데 Call by Value라는 것을 간과하고 있었습니다. ( 이런걸 틀리다니..)

(디버깅하면서 알아차렸다..)

top을 정적변수로 할당하거나 포인터를 사용하는 등Call by Reference를 활용하셔야합니다.

  • gets()함수는 표준적인 입출력 함수가 아니다. 다른것을사용해야 한다.
  • 저는 문자열 중에 ' ( ' , ' [ ' 이 두 괄호일 경우에만 push하고 ' ) ' , ' ] ' 일 경우에는 최근에 stack에 저장된 stack[top]이 ' ) '일 때는 ' ( ' 가 저장되있고, ' ] ' 일때는 ' [ ' 가 저장되있어야 pop이 가능하고 그위에는 no처리 했습니다.
  • scanf()를 사용하면 \n이 저장되어있기에 버퍼를 비워주셔야 합니다. getchar()사용 중요!
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>	
#include <string.h>
int top = -1;
void push(char stack[],char ps) { //'( ' 또는 ' [ ' 일 때 문자열 push
	stack[++top] = ps;
}
void pop() {
	--top;
}
int main() {
	while(1)
	{
		top = -1;				
		char stack[100];
		char str[102];		//str[0]=='.'가 되어야 종료이기 때문에 와일문에서 계속적으로 초기화를 해줍니다.
		scanf("%[^\n]s", &str); //공백포함 문자열을 받고,
		if (str[0] == '.')
			break;
		for (int j = 0; j < 102; j++) {	//문자열 중에서 '(' 이거나 '['일 경우 push()
			if (str[j] == '(')
				push(stack,'(');
			else if (str[j] == '[')
				push(stack, '[');
			else if (str[j] == ')') { //')'일 경우 top의 원소가 '('일 때만 pop아니면 오류처리.
				if (stack[top] == '(')
					pop();
				else {
					printf("no\n");
					break;
				}
			}
			else if (str[j] == ']') { //위에랑 마찬가지.
				if (stack[top] == '[')
					pop();
				else {
					printf("no\n");
					break;
				}
			}
			if (str[j] == '.') {
				if (top == -1) {
					printf("yes\n");
					break;
				}
				else {
					printf("no\n");
					break;
				}
				
			}
		}getchar(); //scanf로 \n입력전까지 문자열 저장이기 때문에 
		//scanf로 문자열을 입력 받은 후에는 버퍼에 \n이 저장되어있습니다. 따라서 이 버퍼의 \n을 처리하지
		//않으면 무한루프를 돌게 됩니다.
	}
	return 0;
	
}

저는 원래 가독성을 추구해서 함수를 만들어서 사용하는 것을 중요하게 생각합니다.

하지만 

백준이라는 문제를 풀다보니 수행시간이 길어지면 오류가 나기 때문에 아직까지는 함수로 사용을 안하고있습니다...

 

여러분들은 함수 만들어서 사용하시나요? 

함수 만들거나 enum등 사용해서 깔끔하게 코드를 구현해도  시간초과, 메모리 초과 등이 안 걸리나요??

댓글로 알려주시면 감사합니다. ^^