stoi 대신 stoll을 사용하여 정수 오버플로우 해결하기 - 크기가 작은 부분문자열 (147355)
문제 설명
숫자로 이루어진 문자열 t와 p가 주어질 때, t에서 p와 길이가 같은 부분문자열 중, 해당 부분문자열이 나타내는 수가 p가 나타내는 수보다 작거나 같은 경우의 개수를 반환하는 함수를 완성하세요.
예를 들어, t="3141592"이고 p="271"인 경우, t에서 길이가 3인 부분 문자열은 314, 141, 415, 159, 592입니다. 이 중 271보다 작거나 같은 수는 141과 159로, 결과는 2가 됩니다.
제한사항
- 1 ≤ p의 길이 ≤ 18
- p의 길이 ≤ t의 길이 ≤ 10,000
- t와 p는 숫자로만 이루어진 문자열이며, 0으로 시작하지 않습니다.
입출력 예
t p result
"3141592" | "271" | 2 |
"500220839878" | "7" | 8 |
"10203" | "15" | 3 |
입출력 예 설명
예제 1
본문과 같습니다.
예제 2
p의 길이가 1이므로 t의 부분 문자열은 각각 "5", "0", "0", "2", "2", "0", "8", "3", "9", "8", "7", "8"입니다. 이 중 7보다 작거나 같은 숫자는 8개입니다.
예제 3
p의 길이가 2이므로 t의 부분 문자열은 "10", "02", "20", "03"이며, 이 중 15보다 작거나 같은 숫자는 "10", "02", "03"으로 3개입니다. "02"와 "03"은 각각 2, 3에 해당합니다.
참고 자료
출처: 프로그래머스 코딩 테스트 연습
문제 상황
최근 특정 문제를 풀면서 signal: aborted (core dumped) 오류를 겪었습니다. 원인은 주어진 문자열에서 특정 길이의 부분 문자열을 추출하여 숫자로 변환 후 비교하는 과정에서 발생한 오버플로우였습니다.
원인 분석
초기 코드에서는 stoi를 사용하여 문자열을 정수로 변환하였습니다.
#include <string>
#include <vector>
using namespace std;
int solution(string t, string p) {
int answer = 0;
int pp = stoi(p); // p를 정수로 변환
vector<int> numbers;
if (t.size() < p.size()) {
return 0;
}
// t에서 p와 같은 길이의 부분 문자열을 추출하여 정수 변환 후 저장
for (int i = 0; i <= t.size() - p.size(); i++) {
string sub = t.substr(i, p.size());
numbers.push_back(stoi(sub));
}
// 변환된 숫자들과 p를 비교하여 조건을 만족하는 개수 세기
for (size_t i = 0; i < numbers.size(); i++) {
if (numbers[i] <= pp) {
answer++;
}
}
return answer;
}
여기서 문제가 발생한 이유는 stoi가 반환하는 자료형이 int이기 때문입니다. 일반적인 32비트 환경에서는 int의 표현 범위가 -2,147,483,648 ~ 2,147,483,647로 제한됩니다. 하지만 p의 길이가 최대 18까지 가능하므로, 일부 숫자는 int의 범위를 초과하여 오버플로우가 발생할 수 있습니다.
예를 들어, stoi("9876543210")을 실행하면 int 범위를 초과하여 예외가 발생합니다. 반면, stoll("9876543210")을 사용하면 long long 범위 내에서 정상적으로 변환됩니다.
해결 방법
오버플로우를 방지하기 위해 더 큰 범위를 지원하는 long long 자료형을 사용해야 합니다. stoi 대신 stoll을 사용하면 long long 범위를 초과하지 않는 한 정확한 변환이 가능합니다.
수정된 코드:
#include <string>
#include <vector>
using namespace std;
int solution(string t, string p) {
int answer = 0;
long long pp = stoll(p); // p를 long long으로 변환
vector<long long> numbers;
if (t.size() < p.size()) {
return 0;
}
// t에서 p와 같은 길이의 부분 문자열을 추출하여 정수 변환 후 저장
for (int i = 0; i <= t.size() - p.size(); i++) {
string sub = t.substr(i, p.size());
numbers.push_back(stoll(sub));
}
// 변환된 숫자들과 p를 비교하여 조건을 만족하는 개수 세기
for (size_t i = 0; i < numbers.size(); i++) {
if (numbers[i] <= pp) {
answer++;
}
}
return answer;
}
stoi vs stoll 차이점
함수명 반환 타입 표현 가능한 숫자 범위 (64비트 환경 기준)
stoi | int | -2,147,483,648 ~ 2,147,483,647 (약 10자리) - stoi("9876543210")은 오류 발생 가능 |
stoll | long long | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 (약 18~19자리) |
따라서 p의 길이가 최대 18자리까지 가능하므로, stoi를 사용하면 int 범위를 초과하여 오버플로우가 발생할 수 있습니다. stoll을 사용하면 더 넓은 범위의 숫자를 다룰 수 있어 이러한 문제를 방지할 수 있습니다.
결론
이 경험을 통해, 정수 변환 시 입력값의 최대 크기를 항상 고려해야 한다는 점을 다시금 깨닫게 되었습니다. 특히 문제에서 숫자 범위가 18자리까지 가능하다면, int가 아니라 long long을 고려해야 한다는 점을 잊지 마세요!