문제
선영이는 주말에 할 일이 없어서 새로운 언어 AC를 만들었다. AC는 정수 배열에 연산을 하기 위해 만든 언어이다. 이 언어에는 두 가지 함수 R(뒤집기)과 D(버리기)가 있다.
함수 R은 배열에 있는 수의 순서를 뒤집는 함수이고, D는 첫 번째 수를 버리는 함수이다. 배열이 비어있는데 D를 사용한 경우에는 에러가 발생한다.
함수는 조합해서 한 번에 사용할 수 있다. 예를 들어, "AB"는 A를 수행한 다음에 바로 이어서 B를 수행하는 함수이다. 예를 들어, "RDD"는 배열을 뒤집은 다음 처음 두 수를 버리는 함수이다.
배열의 초기값과 수행할 함수가 주어졌을 때, 최종 결과를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 테스트 케이스의 개수 T가 주어진다. T는 최대 100이다.
각 테스트 케이스의 첫째 줄에는 수행할 함수 p가 주어진다. p의 길이는 1보다 크거나 같고, 100,000보다 작거나 같다.
다음 줄에는 배열에 들어있는 수의 개수 n이 주어진다. (0 ≤ n ≤ 100,000)
다음 줄에는 [x1,...,xn]과 같은 형태로 배열에 들어있는 정수가 주어진다. (1 ≤ xi ≤ 100)
전체 테스트 케이스에 주어지는 p의 길이의 합과 n의 합은 70만을 넘지 않는다.
출력
각 테스트 케이스에 대해서, 입력으로 주어진 정수 배열에 함수를 수행한 결과를 출력한다. 만약, 에러가 발생한 경우에는 error를 출력한다.
풀이
매번 배열을 실제로 뒤집으면 비효율적이므로, 방향만 논리적으로 뒤집는 방식이 핵심이다. R이 나올 때마다 reverse 플래그만 바꾸고, D가 나오면 현재 방향 기준 앞이나 뒤에서 하나를 제거하면 된다.
코드에서는 덱에 수열을 담아 두고, reverse 여부에 따라 앞 또는 뒤에서 제거한다. 삭제할 원소가 없는데 D를 수행하려 하면 그 즉시 error다.
이 문제는 방향 플래그와 덱으로 명령을 압축하는 구현 문제다.
코드
#include <iostream>
#include <string>
using namespace std;
//배열에서 값이 시작되는 지점(top)과 배열에 값이 들어간 마지막 지점(back)을 이용
int arr[100001];
int top = 0;
int back = 0;
//배열을 직접 바꿀 필요는 없음, R 함수에서 top과 back의 값을 바꿔줘서 뒤집는 효과를 줌
void R() {
int temp = back;
back = top;
top = temp;
}
//is_reverse가 true인 경우 역방향
bool D(bool is_reverse) {
if (is_reverse) {
if (top < back)
return true;
else {
top--;
return false;
}
}
else {
if (top > back)
return true;
else {
top++;
return false;
}
}
}
void solve() {
int t, n;
cin >> t;
char c;
string p, s = "";
bool is_error, is_reverse;
while (t--) {
cin >> p >> n;
top = 0;
back = n - 1;
//n에 0값이 들어온다면 n을 1 증가시켜 뒤의 for문을 돌아 빈 값 '[]'를 입력받도록 함
if (!n)
n++;
for (int i = 0; i < n;) {
cin >> c;
if (c == ',' || c == ']') {
//만약 빈 값 '[]'이라면 stoi 함수를 사용하지 않도록 함
if(s != "")
arr[i] = stoi(s);
i++;
s = "";
}
else {
if (c != '[')
s += to_string(c - '0');
}
}
int size = (int)p.size();
//배열이 비어 있는데 D 함수를 호출한 경우 is_error = true
is_error = false;
//R 함수를 호출하여 역방향으로 바뀌었다면 is_reverse = true
is_reverse = false;
for (int j = 0; j < size; j++) {
switch (p[j]) {
case 'R':
R();
is_reverse = !is_reverse;
break;
case 'D':
is_error = D(is_reverse);
break;
}
if (is_error) {
break;
}
}
//is_error의 값에 따라 배열을 출력할지 error를 출력할지 결정
if (!is_error) {
cout << "[";
//정방향인지 역방향인지에 따라 for문 결정
if (is_reverse) {
for (int k = top; k >= back; k--) {
cout << arr[k];
if (k != back)
cout << ",";
}
}
else {
for (int k = top; k <= back; k++) {
cout << arr[k];
if (k != back)
cout << ",";
}
}
cout << "]" << '\n';
}
else {
cout << "error" << '\n';
}
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
solve();
return 0;
}복잡도
- 시간 복잡도: 명령 수를 , 원소 수를 이라 하면 각 테스트케이스를 에 처리한다.
- 공간 복잡도: 배열 원소를 저장하므로 이다.
마무리
뒤집기를 진짜로 하지 않는 순간 이 문제는 훨씬 쉬워진다. 방향 플래그 하나와 덱만으로 모든 명령을 처리할 수 있다.
