프로그래밍 언어/C++

stoi 대신 stoll을 사용하여 정수 오버플로우 해결하기 - 크기가 작은 부분문자열 (147355)

로안님 2025. 2. 10. 09:38

문제 설명

숫자로 이루어진 문자열 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을 고려해야 한다는 점을 잊지 마세요!