본문 바로가기
Algorithm(코딩테스트)/문자열

[백준] 9093번 단어 뒤집기 <C++>

by 카랑현석 2024. 2. 3.

문제

 

문제 분석 / 시행 착오

문제의 요지는 쉽게 말하자면 문장에서 각 단어마다 반대로 뒤집어서 출력하는 것이 핵심이라고 볼 수 있다.

 

필자는 총 2번의 시행착오를 거쳤는데 이 글을 보는 많은 독자들이 이 2개의 시행착오 중 하나를 겪었을 것이라고 생각한다.

 

시행착오 1 :  문장이 1개만 입력되어지고 프로그램이 종료될 때

분명 예제 케이스를 잘 붙여넣기 했는데 시행착오 1 이 일어나는 경우에는 cin/getline과 cin.ignore() 의 개념을 몰라서 그런 것이다.

 

cin은 강제 개행(즉, '\n')을 처리하지 않고 입력 버퍼로 남겨둔다. (즉, '\n'은 입력 버퍼에 남는다.)

getline은 강제 개행(즉, '\n')을 입력 버퍼에서 가져와 처리한다. (즉, '\n'은 입력 버퍼에 남지 않는다.)

 

	int T;
	cin >> T; // testcase 개수 입력하기
	for (int i = 0; i < T; i++) {
		int start = 0; // 단어를 뒤집을 시작 인덱스
		string s; // 입력받은 문장
		getline(cin, s);
        ... // 이하 코드 생략

예를 들어 위 코드에서 T를 입력 받고 '\n'(enter 키)을 입력 받으면 '\n'은 입력 버퍼에 저장되게 되고 getline(cin, s); 코드에서 그 입력 버퍼를 가져와서 처리하기 때문에 '\n'의 입력으로 getline(cin, s); 구문이 끝나게 되는 것이다.

 

따라서 입력 버퍼에 '\n' 을 없애야 하므로 이럴 때 cin.ignore()을 사용한다. 없애야 하는 입력 버퍼가 여러개면 cin.ignore(3)과 같이 숫자를 인자로 넣어주면 된다. ( cin.ignore(3); 은 3개의 입력 버퍼를 없애준다는 의미이다.)

 

	int T;
	cin >> T; // testcase 개수 입력하기
    	cin.ignore(); // '\n'(강제 개행) 버퍼 없애기
	for (int i = 0; i < T; i++) {
		int start = 0; // 단어를 뒤집을 시작 인덱스
		string s; // 입력받은 문장
		getline(cin, s);
        ... // 이하 코드 생략

 

시행착오 2 :  문장이 1개만 입력되어지고 프로그램이 종료될 때

이번에는 2번째 줄 문장을 입력 받을 때 그 첫 글자인 ('W')가 없어지는 현상이다.

시행착오 1을 제대로 이해했다면 쉽게 해결할 수 있다.

 

이 문제도 cin/getline과 cin.ignore()의 개념을 정확히 몰랐기 때문에 발생한 문제이다.

	int T;
	cin >> T; // testcase 개수 입력하기
	for (int i = 0; i < T; i++) {
		int start = 0; // 단어를 뒤집을 시작 인덱스
		cin.ignore(); // 버퍼 지우기
		string s; // 입력받은 문장
		getline(cin, s);

위와 같이 진행하면 첫 문장에서는 숫자 T를 입력받는 과정에서 입력 버퍼 '\n'이 생기게 되고 for문 안에서 cin.ignore()을 통해 입력 버퍼 '\n'을 지우고 getline(cin, s); 을 통해 문장을 입력받지만 T가 2 이상인 경우, 두 번째 for 문부터는 남아 있는 입력 버퍼가 없으므로(getline은 '\n'을 입력 버퍼로 남기지 않는다.) cin.ignore();을 통해 입력받은 문장의 첫 번째 글자를 지우게 되므로 문장의 첫 번째 글자가 없어지게 된 것이다. cin.ignore(); 의 위치를 신경 써주어야 한다. 

 

	int T;
	cin >> T; // testcase 개수 입력하기
	cin.ignore(); // 버퍼 1글자 지우기
	for (int i = 0; i < T; i++) {
		int start = 0; // 단어를 뒤집을 시작 인덱스
		string s; // 입력받은 문장
		getline(cin, s);

위 코드는 이제 정상적으로 작동하는 코드이다.

정답 코드

- 각 단어마다 인덱스로 접근해 띄어쓰기가 입력되면(' ') 띄어쓰기 직전 단어들을 뒤집어주었다.

- 이렇게 하면 문장의 마지막 단어은 뒤집지 못한 채 마무리 되므로 마지막에 마지막 단어을 뒤집어주었다.

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main(void) {
	ios_base::sync_with_stdio(false); cin.tie(nullptr); // 고속 입출력
	int T;
	cin >> T; // testcase 개수 입력하기
	cin.ignore(); // 입력 버퍼 1개 지우기
	for (int i = 0; i < T; i++) {
		int start = 0; // 단어를 뒤집을 시작 인덱스
		string s; // 입력받은 문장
		getline(cin, s);
		for (int j = 0; j < s.size(); j++) { // 각 문장의 단어 하나마다 검사
			if (s[j] == ' ') { // 띄어쓰기가 나온 경우 그 이전까지의 단어를 뒤집기
				reverse(s.begin() + start, s.begin() + j);
				start = j + 1;
			}
		}
		
		// 마지막 단어 뒤집기
		reverse(s.begin() + start, s.end());
		
		// 최종 문장 출력
		for (int j = 0; j < s.size(); j++) {
			cout << s[j];
		}
		cout << "\n";
	}

	return 0;
}

 



교훈점

- cin, getline, cin.ignore() 에 대해 확실하게 알 수 있는 좋은 문제였다.

- 해당 문제를 스택으로 풀었던 독자가 있다면 문자열로 한 번 풀어보면 좋을 것 같은 문제이다.

- 하나를 알더라도 제대로 알아야 된다는 생각이 들었다.