백준 5430번: AC

kgh128·2023년 2월 4일

코드: https://github.com/kgh128/Problem-Solving/blob/main/src/Baekjoon/p5430.java


1. 입력받기 & 파싱하기

수행할 함수들을 먼저 입력받는다. 모든 함수는 하나의 문자이므로 일단 수행 함수들을 String으로 입력받고, 나중에 함수를 수행할 때 한 문자씩 쪼개서 무슨 함수인지 판단한다.

String functions = br.readLine();

처음에는 split("")으로 파싱했는데. 어차피 하나의 문자가 하나의 함수인 것이 보장되어있으므로 split("")으로 파싱하는 것이 별로 의미가 없다고 판단했다. split()을 쓰면 위의 코드보다 시간도 더 걸려서 위의 코드처럼 수정했다.


배열에 들어있는 수의 개수를 입력받는다.

int arraySize = Integer.parseInt(br.readLine());

배열을 일단 원본 그대로 입력받는다.(inputs) 배열이 []로 감싸져 있으므로 substring(startIndex, endIndex+1)을 써서 [] 안에 있는 내용물만 뽑아낸다. 그리고 그 뽑아낸 내용물을 split(",")을 써서 파싱하여 배열의 숫자 하나를 array 배열의 원소 하나로 넣는다.

String inputs = br.readLine();
String[] array = inputs.substring(1, inputs.length()-1).split(",");
StringTokenizer st = new StringTokenizer(br.readLine(), "[],");

이렇게 한 번에 대괄호와 쉼표를 파싱해도 된다.


2. 덱에 배열 입력

R 함수 때문에 배열을 뒤집어야 하는데, 실제로 배열을 뒤집는 것은 비효율적이다.

  • 정방향일 때 D 수행: 배열의 가장 앞에 있는 원소 삭제
  • 역방향일 때 D 수행: 배열의 가장 뒤에 있는 원소 삭제

이런 형식으로 동작하게 하여 배열을 뒤집은 효과를 주려고 하였다. 그러기 위해서 양방향으로 데이터를 입출력 할 수 있는 덱(Deque)을 사용하였다.

덱에 초기 배열을 입력한다. 만약 초기 배열이 빈 배열이면 array에 있는 첫번째 원소가 ""이다. Integer.parseInt("")는 수행할 수 없으므로 예외가 발생한다. 그래서 빈 배열이 아닐 때만 array의 원소를 정수로 바꿔서 덱에 넣도록 하였다.

Deque<Integer> deque = new ArrayDeque<>(arraySize);

if (arraySize > 0) {
	for (String num: array) {
		deque.add(Integer.parseInt(num));
	}
}

초기 배열이 빈 배열인 경우를 고려하지 않아서 위에 설명한 것처럼 예외가 발생하였다.


3. 함수 수행 (덱 연산)

현재 deque를 역방향으로 사용하고 있는지를 저장하는 변수인 isReverseDeque를 이용하였다.

  • 정방향: isReverseDeque = false;
  • 역방향: isReverseDeque = true;

함수들을 저장하고 있는 functions 문자열을 char형 배열로 바꿔서 각 문자(함수)를 돌면서 수행하였다.

  • R 수행: isReverseDeque의 값을 반대로 바꾼다. (true이면 false로, false이면 true로)
  • 정방향일 때 D 수행: 배열의 가장 앞에 있는 원소 삭제 (removeFirst())
  • 역방향일 때 D 수행: 배열의 가장 뒤에 있는 원소 삭제 (removeLast())

Deque에서 덱이 비어있을 때 remove 계열 메소드를 사용하면 예외가 발생한다. 이를 이용하여 배열이 비어있을 때 D를 사용한 경우를 처리하였다. 함수를 수행하는 for문 자체를 try 블록 안에 넣고, 그 안에서 예외가 발생하면 에러가 난 경우이므로 catch 블록에서 출력 버퍼에 "error"를 저장하고, 현재 테스트 케이스를 탈출하였다.

boolean isReverseDeque = false;

try {
	for (char func : functions.toCharArray()) {
		if (func == 'R') {
			isReverseDeque = !isReverseDeque;
		}
		else if (func == 'D' && !isReverseDeque) {
			deque.removeFirst();
		}
		else {
			deque.removeLast();
		}
	}
} catch (Exception e) {
	bw.append("error\n");
	continue;
}

4. 최종 결과 배열 저장

최종 결과 배열도 처음에 입력받은 배열 형식으로 출력해야 한다. 그래서 일단 출력 버퍼에 [를 추가하였다. 그리고 덱에 있는 최종 결과 배열을 for문을 돌면서 출력 버퍼에 저장하였다.

  • 정방향: 앞에 있는 원소부터 저장
  • 역방향: 뒤에 있는 원소부터 저장
bw.append("[");
int dequeSize = deque.size();

if (!isReverseDeque) {
	for (int j = 0; j < dequeSize; j++) {
		bw.append(deque.removeFirst()).append(',');
	}
}
else {
	for (int j = 0; j < dequeSize; j++) {
		bw.append(deque.removeLast()).append(',');
	}
}

처음에는 for문 종료 조건에 바로 deque.size()를 넣었다. 이러면 반복문 돌면서 원소가 하나씩 줄어들기 때문에, 현재 덱의 크기인 deque.size()도 반복문을 돌면서 하나씩 줄어들었다. 그래서 생각했던 것 만큼 반복문을 돌지 않고 끝나서 결과가 제대로 출력되지 않았다.

그래서 최종 결과 배열이 다 들어있을 때 덱의 크기를 dequeSize에 저장해놓고, 그 변수를 for문의 종료 조건으로 사용하였다.


반복문을 돌 때 배열의 숫자와 함께 쉼표도 출력 버퍼에 저장하기 때문에 마지막 숫자 뒤에도 쉼표가 있다. 이 쉼표는 제거해줘야 하므로 deleteCharAt()을 이용하였다. 최종 결과 배열이 빈 배열이면 반복문을 안돌아서 출력 버퍼에 쉼표가 없다. 그러므로 이 경우는 제외하고 쉼표 제거를 해줘야 한다.

마지막으로 출력 버퍼에 ]를 추가하였다.

if (dequeSize > 0) {
	bw.deleteCharAt(bw.length() - 1);
}
bw.append("]\n");

최종 결과 배열이 빈 배열인 경우를 고려하지 않고 쉼표 제거를 했다가 [가 제거되고 결과로 ]만 나온 케이스도 있었다. 그래서 dequeSize가 0보다 큰 경우만 쉼표 제거를 하도록 수정하였다.

처음에는 초기 배열 크기인 arraySize가 0보다 큰 경우에 쉼표 제거를 하도록 했는데, 이러면 초기 배열은 빈 배열이 아닌데 D를 계속 수행해서 결과 배열이 빈 배열이 되는 경우를 걸러주지 못했다. 그래서 결과 배열의 크기인 dequeSize를 이용하였다.

0개의 댓글