경쟁 프로그래밍이나 코딩 테스트에서는 문제 해결 능력만큼이나 코드를 얼마나 빠르게 작성하느냐가 중요합니다. 잘 알려진 C++의 숨은 기능들을 활용하면 불필요한 구현 시간을 줄이고 실수 가능성도 낮출 수 있습니다. 이 글에서는 C++11 기준으로 실전에서 바로 쓸 수 있는 유용한 트릭들을 하나씩 소개합니다.
1. % 연산자 없이 홀수·짝수 판별하기
나머지 연산자(%)를 쓰지 않고도 비트 AND 연산으로 홀수와 짝수를 구분할 수 있습니다. 모든 홀수는 최하위 비트(LSb)가 1이므로, 숫자와 1을 AND 연산하면 최하위 비트만 남게 됩니다. 결과가 0이 아니면 홀수, 0이면 짝수입니다.
if ((n & 1) != 0) {
// 홀수
} else {
// 짝수
}
2. 시프트 연산자로 곱셈·나눗셈 빠르게 처리하기
어떤 수에 2n을 곱하려면 왼쪽으로 n번 시프트하고, 2n으로 나누려면 오른쪽으로 n번 시프트하면 됩니다. 곱셈·나눗셈 연산보다 비트 시프트가 더 빠르게 처리됩니다.
int x = 40; int y = x << 2; // 40 * 4 = 160 cout << y << endl; // 160 y = x >> 2; // 40 / 4 = 10 cout << y << endl; // 10
3. 임시 변수 없이 두 수 교환하기
덧셈과 뺄셈으로도 두 변수를 교환할 수 있지만, 비트 XOR 연산을 사용하면 더 간결하게 처리할 수 있습니다.
// x와 y 교환 x ^= y; y ^= x; x ^= y;
4. strlen() 없이 문자열 순회하기
strlen() 함수를 사용할 수 없는 제약 조건이 있더라도 걱정할 필요가 없습니다. C 스타일 문자열은 널 문자('\0')로 끝나므로, 해당 위치의 문자가 유효한 값(0이 아닌 값)인지만 검사하면 됩니다.
for (int i = 0; s[i]; i++) {
cout << s[i];
}
5. push_back() 대신 emplace_back() 사용하기
STL 컨테이너(vector 등)에 새 요소를 추가할 때 흔히 push_back()을 사용하지만, emplace_back()을 사용하면 더 빠릅니다. 이 함수는 별도의 임시 메모리를 할당하지 않고 컨테이너 내부에서 직접 객체를 생성해 성능상 이점이 있습니다.
6. 내장 GCD 함수 활용하기
C++은 최대공약수(GCD)를 구하는 내장 함수를 제공합니다. 직접 유클리드 호제법을 구현할 필요 없이 다음과 같이 바로 사용할 수 있습니다.
__gcd(x, y) // x와 y의 최대공약수 반환
7. 배열 크기 제한 이해하기
main 함수 안에서 지역 배열로 선언하면 스택 크기 제한 때문에 약 106 정도까지만 선언할 수 있습니다. 하지만 배열을 전역(global)으로 선언하면 최대 107 크기까지 확보할 수 있습니다. 큰 배열이 필요한 문제라면 전역 선언을 활용하세요.
8. log10으로 최상위 자릿수 구하기
로그 연산을 이용하면 어떤 수의 가장 앞자리 숫자(최상위 자릿수)를 쉽게 구할 수 있습니다.
int n = 4578; double k = log10(n); k = k - floor(k); int x = pow(10, k); // x = 4 (최상위 자릿수)
9. 반복문 없이 자릿수 개수 구하기
반복문으로 한 자리씩 세는 대신 로그를 사용하면 한 줄로 자릿수를 계산할 수 있습니다.
int n = 4578; int digit_count = floor(log10(n)) + 1; // 4
10. 2의 거듭제곱 여부 확인하기
2의 거듭제곱인 수는 이진 표현에서 비트가 하나만 1입니다. 따라서 x와 (x - 1)을 AND 연산하면 0이 되는 성질을 이용해 간단히 판별할 수 있습니다.
int x = 1024; bool check = x && (!(x & (x - 1))); // true이면 2의 거듭제곱
11. all_of, any_of, none_of 조건 검사 알고리즘
C++은 배열 전체의 조건 만족 여부를 검사하는 내장 알고리즘을 제공합니다. 반복문을 직접 작성할 필요가 없습니다.
all_of(left, left + n, isPositive()); // 모든 요소가 양수인지 검사 any_of(left, left + n, isPositive()); // 양수인 요소가 하나라도 있는지 검사 none_of(left, left + n, isPositive()); // 양수인 요소가 하나도 없는지 검사
12. copy_n으로 컨테이너 복사하기
copy_n() 함수를 사용하면 지정한 개수만큼의 요소를 한 컨테이너에서 다른 컨테이너로 손쉽게 복사할 수 있습니다.
int src[5] = {10, 20, 30, 40, 50};
int dest[5];
copy_n(src, 5, dest);
13. iota로 연속된 값 생성하기
iota() 알고리즘은 시작 값을 첫 번째 요소에 저장한 뒤, 후위 증가 연산자처럼 값을 1씩 늘려가며 범위를 채웁니다. 숫자뿐 아니라 문자에도 사용할 수 있습니다.
int arr[5] = {0};
iota(arr, arr + 5, 15); // {15, 16, 17, 18, 19} 생성
char str[5] = {0};
iota(str, str + 5, 'A'); // {'A', 'B', 'C', 'D', 'E'} 생성
14. 0b 접두사로 이진수 리터럴 사용하기
C++11부터는 숫자 앞에 0b 접두사를 붙여 이진수를 직접 표현할 수 있습니다. 비트 연산 문제에서 가독성이 크게 향상됩니다.
int x = 0b1101; // x에는 13이 저장됨
15. and 같은 대체 키워드 사용하기
C++에서는 조건 연산자 대신 읽기 쉬운 키워드 형태의 대체 표기를 사용할 수 있습니다. 예를 들어 '&&' 대신 'and'를 쓸 수 있습니다.
int x = 10;
if (x < 20 and x > 5)
cout << "True" << endl;
else
cout << "False" << endl;
// 출력: True
이 외에도 C++에는 경쟁 프로그래밍에 도움이 되는 다양한 내장 기능들이 있습니다. 위 트릭들을 미리 익혀두면 시험장에서 구현 시간을 크게 단축하고, 더 중요한 알고리즘 설계에 집중할 수 있습니다.