<aside> 😮 I/O는 예술이다. C++로부터, C, 그리고 심지어 시스템 호출까지 내려가 보자. 점점 아래로 내려갈수록 속도는 빨라지고 우리가 느끼는 기쁨은 2배가 된다(속도가 2배씩 빨라지기 때문이다). 이 기쁨을 함께 누리고자 글을 쓰게 되었으니, 바로 출발해 보자.

</aside>

Table of Contents

문제 소개 (백준 1152번)

1152번: 단어의 개수

문제가 중요한 것은 아니므로 빠르게 훑고 넘어가 보자. 주어진 문자열의 단어의 개수를 세는 문제이다. 단어의 공백은 ‘ ‘, 즉 스페이스로 구분된다. 여기에서 함정은 단어가 공백으로 시작하거나 끝날 수 있는데, 공백이 연속해서 나오지 않는다고 한다. 한 마디로 "hello "과 같은 경우나 "hello" 경우가 존재한다는 건데, 단어 하나하나 말고 주어진 문자열 전체의 앞뒤로 튀어나온 스페이스들만 주의하면 될 것이다.

문제 소개는 이정도만 하고 바로 출발해 보자.

가장 우아한 방법 (istream::operator>>, ~12ms)

#include <iostream>
using namespace std;

int main()
{
    int wc = 0;
    string dummy;
    for (; cin >> dummy; ++wc) { }
    cout << wc;
}

군더더기가 하나 없다. 아마 c++에서 이 문제를 표현할 수 있는 가장 깔끔한 방법인 것 같다. 바로 cin의 operator>>가 자동으로 스페이스를 손질해 준다는 점을 이용한 건데, 이 때문에 문제의 추가 조건에 대해서 신경쓸 일이 없다. for 대신 while 을 사용하는 방법도 있겠다.

사실 우아해 보이려고 생략했는데, 이 코드대로 실행하면 40ms가 나오고, 12ms가 나오려면 ios::sync_with_stdio(false) , cin.tie(nullptr) 을 적용해 주어야 한다. 혹시 이 둘을 처음 보는 사람이 있다면 cin.tie(nullptr)? 그거 왜 하는건데? (백준 문제풀이)를 한번 읽어보자.

(wc는 word count의 줄임말이고, 리눅스의 명령어이기도 한 만큼 줄여 썼다.)

한 계단 내려가기 (istream::ignore, ~8ms)

High-level에서 low-level로 한 계단만 내려가 보자. 우아한 operator>> 를 이용하는 것은 좋지만, 위 코드가 비효율적이라는 것을 눈치챘는가? 바로 우리가 사용할 일이 전혀 없는 string dummy 에 계속해서 string 을 저장하고 있었다는 점이다. 저장하는 대신에 무시하는 방법이 있다.

#include <iostream>
constexpr auto STR_MAX = 0x7fffffffffffffff;
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int wc = 0;
    for (; (cin >> ws).ignore(STR_MAX, ' '); ++wc) { }
    cout << wc;
}

혹시 이 방법에 대해 별로 궁금하지 않거나 무슨 말인지 이해가 안된다면 아래 굵은 글씨로 된 문단까지 뛰어넘어도 무관하다. 별로 중요한 내용이 아니다.

위에서 2번째 줄의 STR_MAX는 i/o stream 객체의 최대 길이(stream max)인데, <iostream>헤더의 그 stream이냐고 묻는다면 맞다. 바로 stream이 지닐 수 있는 최대 길이를 뜻한다.