#2504 괄호의 값
난이도 : 실버 2
유형 : 자료구조/ 스택
▸ 문제
4개의 기호 ‘(’, ‘)’, ‘[’, ‘]’를 이용해서 만들어지는 괄호열 중에서 올바른 괄호열이란 다음과 같이 정의된다.
- 한 쌍의 괄호로만 이루어진 ‘()’와 ‘[]’는 올바른 괄호열이다.
- 만일 X가 올바른 괄호열이면 ‘(X)’이나 ‘[X]’도 모두 올바른 괄호열이 된다.
- X와 Y 모두 올바른 괄호열이라면 이들을 결합한 XY도 올바른 괄호열이 된다.
예를 들어 ‘(()[[]])’나 ‘(())[][]’ 는 올바른 괄호열이지만 ‘([)]’ 나 ‘(()()[]’ 은 모두 올바른 괄호열이 아니다. 우리는 어떤 올바른 괄호열 X에 대하여 그 괄호열의 값(괄호값)을 아래와 같이 정의하고 값(X)로 표시한다.
- ‘()’ 인 괄호열의 값은 2이다.
- ‘[]’ 인 괄호열의 값은 3이다.
- ‘(X)’ 의 괄호값은 2×값(X) 으로 계산된다.
- ‘[X]’ 의 괄호값은 3×값(X) 으로 계산된다.
- 올바른 괄호열 X와 Y가 결합된 XY의 괄호값은 값(XY)= 값(X)+값(Y) 로 계산된다.
예를 들어 ‘(()[[]])([])’ 의 괄호값을 구해보자. ‘()[[]]’ 의 괄호값이 2 + 3×3=11 이므로 ‘(()[[]])’의 괄호값은 2×11=22 이다. 그리고 ‘([])’의 값은 2×3=6 이므로 전체 괄호열의 값은 22 + 6 = 28 이다.
여러분이 풀어야 할 문제는 주어진 괄호열을 읽고 그 괄호값을 앞에서 정의한대로 계산하여 출력하는 것이다.
▸ 입력
첫째 줄에 괄호열을 나타내는 문자열(스트링)이 주어진다. 단 그 길이는 1 이상, 30 이하이다.
▸ 출력
첫째 줄에 그 괄호열의 값을 나타내는 정수를 출력한다. 만일 입력이 올바르지 못한 괄호열이면 반드시 0을 출력해야 한다.
문제 풀이
기본적인 문제의 본질은 간단한 데 어떻게 구현하느냐가 까다로웠다. 문제만 읽었을 때는 쉬워보였는데 나름 생각을 많이하게 만드는 문제였다.
초기 아이디어는 다음과 같았다.
- (', '['를 만나면 st에 해당 괄호를 저장한다.
- ')'을 만난다.
- st 앞에 숫자가 있으면 괄호가 닫힐 때 까지 ('('를 만날 때까지) 숫자들을 곱해준다.
- '('를 만나면 해당 숫자를 곱해주고 저장해준다.
- ']'을 만난다.
- st 앞에 숫자가 있으면 괄호가 닫힐 때 까지 ('['를 만날 때까지) 숫자들을 곱해준다.
- '['를 만나면 해당 숫자를 곱해주고 저장해준다.
- st.peek()과 그 다음 값이 숫자라면 값을 더해준다.
그런데 반례가 있었다. 만약 stack에 숫자와 괄호를 동시에 저장해놓으면 나중에 가서 '(', '['가 숫자인지 괄호인지 구분을 못하게 된다.
컴파일러가 각 괄호를 '(' :40, '[':41 인 숫자로 인식하여 처리하기 때문이다.
반례
(((()))(([])))(([[]]))[]()
해당 연산은 앞에 40까지 계산하게 되면 (로 인식하여 로직이 돌아간다. 그래서 괄호를 닫을 때 하나 더 인식해서 에러처리가 된다. 이 부분에 대해 예외처리를 해줘야 하는데 그러면 코드가 점점 산으로 갈 것 같았다.
( ((())) (([])) ) : 2(8+12) = 40
(([[]]))[]() = 36+3+2 = 41
정답 : 81
오답 : 0
오답 코드 펼쳐보기↓ (37% 오답)
import java.io.*;
import java.util.Stack;
public class Main {
static boolean flag;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
Stack<Character> st = new Stack<>();
flag = true;
if(line.length()/2 == 1) flag=false;
for(int i=0; i<line.length(); i++) {
char cur = line.charAt(i);
if(flag) {
if(cur == '(') {
st.push(cur);
}
else if(cur == '[') {
st.push(cur);
}
else if(cur == ')') {
check(st, cur);
}else if(cur == ']') {
check(st, cur);
}
else {
flag = false;
break;
}
if(!st.isEmpty() && st.peek()!='(' && st.peek()!='[') {
int f = st.pop()-48;
int num = f;
while(!st.isEmpty() && st.peek()!='(' && st.peek()!='[') {
if(st.peek() =='2' || st.peek() =='3') {
int tmp = st.pop()-48;
num += tmp;
}else break;
}
char putNum = (char)(num+48);
st.push(putNum);
}
}
}
if(!flag) {
System.out.println(0);
}else {
int answer=0;
while(!st.isEmpty()) {
int num = (int)(st.pop()-48);
if(num >0) answer += num;
else {
System.out.println(0);
return;
}
}
System.out.println(answer);
}
}
static void check(Stack<Character> st, char cur) {
if(!st.isEmpty() && (st.peek() == '(' || st.peek() == '[') ) {
if(cur==')' && st.peek()=='(') {
st.pop();
st.push('2');
return;
}
else if(cur==']' && st.peek()=='[') {
st.pop();
st.push('3');
return;
}else {
flag = false;
return;
}
}
else {
int tmp = 0;
if(!st.isEmpty() &&st.peek()!='(' && st.peek()!='[') {
tmp = st.pop()-48;
if(!st.isEmpty()) {
if(cur==')' && st.peek()=='(') {
tmp *= 2;
}
else if(cur==']' && st.peek()=='[') {
tmp *= 3;
}else {
flag=false;
return;
}
st.pop();
}
}else {
flag = false;
return;
}
char putNum = (char)(tmp+48);
st.push(putNum);
}
}
}
그래서 구현을 하다가 이 방식은 도무지 아닌 것 같아서 아이디어를 갈아엎었다. 괄호의 아스키코드랑 숫자랑 혼동되기 때문에 스택에 연산되는 숫자를 저장해주지 않고 따로 데이터를 다뤄야한다. 분배법칙이라는 힌트를 얻고 다시 새롭게 작성하여 풀이하였다.
해당 연산은 결국 2*(2+ 3*3) + 2*3이다. 그러면 괄호가 닫힐 때마다 현재까지의 곱을 곱해주면 되는 것이다. 괄호가 닫히기 시작하면 해당 저장해놓은 값들을 answer데이터에 옮기고 나머지 괄호가 닫힐 때마다 저장해놓은 곱셈 값들을 정리해준다.
구상
- '(', '['를 만나면 st에 해당 괄호를 넣고 각 2,3의 값을 tmp에 곱해준다.
- ')'을 만난다.
- st가 비어있거나 st.peek() == '('이 아니면 올바른 괄호가 아니므로 에러
- st.peek() == '('
- 바로 이전 값이 ')'이면 지금까지 저장해놓은 값을 answer에 저장해준다. answer += tmp
- 괄호가 닫힐 때마다 저장해준 값들을 정리해준다. tmp /= 2
- ']'을 만난다.
- st가 비어있거나 st.peek() == '['이 아니면 올바른 괄호가 아니므로 에러
- st.peek() == '['
- 바로 이전 값이 ']'이면 지금까지 저장해놓은 값을 answer에 저장해준다. answer += tmp
- 괄호가 닫힐 때마다 저장해준 값들을 정리해준다. tmp /= 3
- 결과값을 출력해준다.
- 에러이거나 st이 비어있지 않으면 0 출력
- 올바른 괄호일 경우 answer 출력
풀이 코드
import java.io.*;
import java.util.Stack;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
Stack<Character> st = new Stack<>();
boolean flag = true;
int answer = 0;
int cnt =1;
for(int i=0; i<line.length(); i++) {
char cur = line.charAt(i);
if(cur == '(') {
st.push(cur);
cnt *= 2;
}
else if(cur == '[') {
st.push(cur);
cnt *= 3;
}
else {
if(cur == ')') {
if(st.isEmpty() || st.peek() != '(') {
flag=false;
break;
}
if(line.charAt(i-1) =='(') {
answer += cnt;
}
st.pop();
cnt /= 2;
}else if(cur==']') {
if(st.isEmpty() || st.peek() != '[') {
flag=false;
break;
}
if(line.charAt(i-1)=='[') {
answer += cnt;
}
st.pop();
cnt /= 3;
}
else {
flag = false;
break;
}
}
}
if(!flag || !st.isEmpty()) {
System.out.println(0);
}else {
System.out.println(answer);
}
}
}
'Dot Algo∙ DS > PS' 카테고리의 다른 글
[BOJ] 백준 2579번 계단 오르기 (Java) (0) | 2021.07.28 |
---|---|
[BOJ] 백준 1302번 베스트셀러 (Java) (0) | 2021.07.27 |
[BOJ] 백준 2263번 트리의 순회 (Java) (2) | 2021.07.25 |
[BOJ] 백준 1991번 트리 순회 (Java) (0) | 2021.07.25 |
[BOJ] 백준 1194번 달이 차오른다, 가자 (Java) (0) | 2021.07.25 |