⛔ 단순히 기록용 입니다... 어떻게 풀었는가 생각도 다시 해보고 그러니까 아마도 도움은 안되실 것 같습니다.
일단 입력값을 보자마자 일반적인 방식으로 풀 수 없다고 생각했다.
N의 크기 (1 <= N <= 100,000,000)과 K (1 <= K<= 1,000,000,000)이 주어진다. N은 1억이고 K는 10억이다. 일반적으로 10억번째의 배열을 생각할 수 있을까? 10억개를 담을 수 있는 것이 있을까? 일반적으로는 말이 되지 않는다. 그래서 생각을 여러번 거쳤다.
일단 12345678910111213141516...9991000 이런식으로 문자열을 생성할 수 있어야한다. 그래서 천천히 생각을 해보았다. 흠 여러여러 돌아다니다가 결국엔 이렇게 해결할 수 밖에 없지 않을까 생각했다.
1개의 자리수 (1 ~9)
2개의 자리수 (10~99)
3개의 자리수 (100~999)
4개의 자리수 (1,000~9,999)
5개의 자리수 (10,000~99,999)
6개의 자리수 (100,000~999,999)
7개의 자리수 (1,000,000~9,999,999)
8개의 자리수(10,000,000~99,999,999)
9개의 자리수(100,000,000)
이렇게 구할수 밖에 없겠다 생각을 했다. 만약 n의 값이 주어졌다면? 그 자리수 만큼 길이를 구해주는 것이다. 이런식으로 진행을 해보자. 예를 들어 입력으로 주어진 것은 n이 20이다. 즉 20까지 이어서 문자열을 만들어야하는데 그 길이가 얼마가 될까?
1~9까지의 길이는 (9 - 1) + 1 = 9이다. 1자리 수이기 때문에 * 1 을 해준다. 그렇다면 총 길이는 9
10~99 까지의 길이는 (99 - 10) + 1 = 90이고, 2자리 수이기 때문에 * 2를 해준다. 그렇다면 총 길이는 9 + 180 = 189
이런식으로 값을 구하면 될 것이다. 그래서 그 길이를 구할 수 있는 코드는 이러하다.
일단 마지막 숫자를 구해야한다. 그래서 그 숫자의 end는 시작하는 값 * 10 - 1 은 마지막 값이 된다. 하지만 그 마지막 값이 우리가 원하는 숫자보다 크다면? end는 우리가 원하는 숫자가 되고, 그 숫자에서 방금 위에 작성한 공식을 적용하면 된다.
그래서 나온 공식은 ((마지막 숫자 - 그 자리수의 첫번째 숫자) + 1 ) * 자리수 가 될 것이다.
그렇다면 우리는 이제 총 길이를 구하는 코드를 작성했다는 것이다. 코드는 아래와 같다.
int getLength(int number) {
int len = 0, n_len = 1;
for(int i = 1; i <= number; i *= 10){
int end = i * 10 - 1;
if(end > number) end = number;
len += ((end - i) + 1 ) * n_len;
n_len++;
}
return len;
}
이제 길이는 구했는데, 과연 그게 몇번째인지는 어떻게 구할 수 있을까? 일단 그 숫자까지 쭉 나열해서 값을 구했고 그런 다음에 23번째를 찾으면 될까? 라는 생각을 했다. 하지만? k의 범위가 10억까지니까 일단 10억까지 나온다는 것 아닐까? 그렇다면 만약에 10억번째를 찾으라고 한다면? 그것을 구할 수 있을까? 절대 없을 것 같다. 그래서 이제는 과연 우리가 원하는 k번째 위치는 어떻게 구할 수 있을까?
우리는 일단 n번까지 총 길이를 구할 수 있었다. 그렇다면? 이때 우리가 원하는 k번째의 위치를 빠르게 찾을 수 있는 방법으로 이분탐색을 생각해볼 수 있겠다. 그렇다면 log (문자열의 길이)이기 때문에 문제를 시간내로 해결할 수 있을 것이다.
그렇다면 start와 end 값을 어떻게 결정해야할까? k의 값이 23이라면? 23번째를 구하는 것이니까 23번째가 될 수 있는 1부터 n까지 중에 절반씩 잘라가면서 중간값이 우리가 원하는 k의 값인 23보다 작다면? start를 늘려나가고, k가 23보다 크다면? end를 줄여나가는 방식을 취해서, 방금 만든 getLength함수를 사용해 길이를 구해서 문제를 해결할 수 있을 것이다.
그래서 아래의 이분 탐색으로 우리는 그것을 구할 수 있다. 어떤 숫자인지를 찾을 수 있다.
int answer = 0;
while(start <= end) {
int mid = (start + end) / 2;
int length = getLength(mid);
if(length < k){
start = mid + 1;
} else{
answer = mid;
end = mid - 1;
}
}
그렇다면 이제 마지막이다. 우리는 그 숫자를 찾았으니 그 숫자의 몇번째가 k일까?
여기서 엄청난 위기가 찾아왔다. 모르겠다는 것이다. 어떻게 구할 수 있을까? 일단 우리가 구한것은 이러하다.
1. 그 해당 위치에 있는 숫자
이것을 위해서 여기까지 온 것이다. 그렇다면 그 숫자까지 오는 중에 하나가 뭐가 될거 아니야?
어쨌든 여기까지 오면서 그 해당 위치에 있는 숫자가 16이다. 여기까지의 길이가 무엇일까?
12345678910111213141516 즉 6이 나와줘야 한다는 것이다. 그렇다면 어떤 공식이 필요할까?
아주아주 한참한참 고민을 했는데, 일단 16이라는 값을 string으로 만들어보자.
'16'인데 그렇다면 이 값은 [1][6] 인데, 마지막을 가리키고 있으면서 ' 12345678910111213141516'의 길이에서 k번째 만큼 자른다면? 그 위치가 되지 않을까? 라는 생각을 겁나했지만?
전체 길이에서 몇번째까지 가야할지는 알고 있지? 전체 길이 - k번재 만큼 자르면? 그게 그 위치가 될 것이고?
그렇다면 정답이 될 것이다. 에서 바로 틀려버렸다. str[총길이 - k번째] 어라라? 그러면 0번째가 되는구나? 1이 나와야하는데 그렇다면 맨 뒤에서 부터 체크를 해주면 되지 않을까?
str[str.size() - 1 - (총길이 - k)] 이런식으로 한다면? 정답이다. 왜인지는 아직도 모른다.. 그냥 두가지를 한번 써봤다.
#include<iostream>
#include<string>
using namespace std;
int n,k;
void init() {
cin >> n >> k;
}
int getLength(int number) {
int len = 0, n_len = 1;
for(int i = 1; i <= number; i *= 10){
int end = i * 10 - 1;
if(end > number) end = number;
len += ((end - i) + 1 ) * n_len;
n_len++;
}
return len;
}
void solve(int start, int end) {
int answer = 0;
while(start <= end) {
int mid = (start + end) / 2;
int length = getLength(mid);
if(length < k){
start = mid + 1;
} else{
answer = mid;
end = mid - 1;
}
}
int c = getLength(answer);
string str = to_string(answer);
cout << str[str.size() - 1 - (c - k)];
return;
}
int main() {
init();
if(getLength(n) < k){
cout << -1;
return 0;
}
solve(1, n);
return 0;
}
'PS > BaekJoon' 카테고리의 다른 글
[C++/25565] 딸기와 토마토 (0) | 2024.06.19 |
---|---|
[C++/5766] 할아버지는 유명해! (0) | 2024.06.17 |
[C++/31937] 로그프레소 마에스트로 (1) | 2024.06.13 |
[C++/1347] 미로 만들기 (1) | 2024.06.10 |
[C++/1660] 캡틴 이다솜 (0) | 2024.06.03 |