어떤 수 n이 주어졌을 때, n보다 작거나 같은 수 중에서 가장 가까운 정돈된 수(Tidy Number)를 찾아야 합니다. 여기서 정돈된 수란 모든 자릿수가 내림차순이 아닌 오름차순(비감소 순서)으로 정렬되어 있는 수를 의미합니다.
예를 들어 입력값이 45000이라면, 각 자릿수가 4 → 5 → 0 → 0 → 0 순서이므로 5 뒤에 0이 오면서 조건을 위반합니다. 따라서 45000보다 작거나 같으면서 가장 가까운 정돈된 수는 44999(4 → 4 → 9 → 9 → 9)가 됩니다.
해결 접근 방식
이 문제는 숫자를 뒤에서부터(오른쪽에서 왼쪽으로) 탐색하면 효율적으로 해결할 수 있습니다. 탐색 과정은 다음과 같습니다.
1. 문자열의 끝에서 두 번째 자릿수부터 시작하여 앞쪽으로 이동합니다.
2. 현재 자릿수가 바로 뒤의 자릿수보다 크면 정돈된 수의 성질이 깨진 것입니다.
3. 이 경우 해당 자릿수를 1 감소시키고, 그 뒤에 있는 모든 자릿수를 전부 '9'로 바꿉니다.
4. 탐색이 끝난 후 결과 문자열을 반환합니다.
이렇게 하면 항상 n보다 작거나 같은 수 중에서 가장 큰 정돈된 수를 얻을 수 있습니다.
C++ 구현 예제
#include <iostream>
using namespace std;
string tidyNum(string number) {
for (int i = number.length() - 2; i >= 0; i--) {
if (number[i] > number[i+1]) {
number[i]--;
for (int j = i + 1; j < number.length(); j++)
number[j] = '9';
}
}
return number;
}
int main() {
string str = "45000";
string num = tidyNum(str);
cout << "정돈된 수: " << num;
}실행 결과
정돈된 수: 44999
동작 원리 상세 설명
입력 "45000"을 기준으로 알고리즘의 진행 과정을 살펴보겠습니다.
- 인덱스 3(숫자 '0')과 인덱스 4(숫자 '0')를 비교: '0' ≤ '0'이므로 통과
- 인덱스 2(숫자 '0')와 인덱스 3(숫자 '0')을 비교: '0' ≤ '0'이므로 통과
- 인덱스 1(숫자 '5')과 인덱스 2(숫자 '0')를 비교: '5' > '0'이므로 위반 → '5'를 '4'로 감소하고 뒤의 자릿수들을 모두 '9'로 변경 → "44000"
- 인덱스 0(숫자 '4')과 인덱스 1(숫자 '4')을 비교: '4' ≤ '4'이므로 통과
최종적으로 "44999"가 출력됩니다.
시간 복잡도
자릿수 개수를 d라고 할 때, 외부 루프가 O(d), 내부 루프 역시 최악의 경우 O(d)이므로 전체 시간 복잡도는 O(d²)입니다. 다만 실제로는 대부분의 경우 한 번의 수정만 발생하므로 사실상 O(d)에 가깝게 동작하며, 공간 복잡도는 입력 문자열 그대로 사용하므로 O(1)의 추가 공간만 필요합니다.