You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
2.4 KiB

2 years ago
package class49;
// 这道题在leetcode上所有题解都只能做到O( (logN) 平方)的解
// 我们课上讲的是O(logN)的解
// 打败所有题解
public class Problem_0440_KthSmallestInLexicographicalOrder {
public static int[] offset = { 0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
public static int[] number = { 0, 1, 11, 111, 1111, 11111, 111111, 1111111, 11111111, 111111111, 1111111111 };
public static int findKthNumber(int n, int k) {
// 数字num有几位len位
// 65237, 5位len = 5
int len = len(n);
// 65237, 开头数字6first
int first = n / offset[len];
// 65237左边有几个
int left = (first - 1) * number[len];
int pick = 0;
int already = 0;
if (k <= left) {
// k / a 向上取整-> (k + a - 1) / a
pick = (k + number[len] - 1) / number[len];
already = (pick - 1) * number[len];
return kth((pick + 1) * offset[len] - 1, len, k - already);
}
int mid = number[len - 1] + (n % offset[len]) + 1;
if (k - left <= mid) {
return kth(n, len, k - left);
}
k -= left + mid;
len--;
pick = (k + number[len] - 1) / number[len] + first;
already = (pick - first - 1) * number[len];
return kth((pick + 1) * offset[len] - 1, len, k - already);
}
public static int len(int n) {
int len = 0;
while (n != 0) {
n /= 10;
len++;
}
return len;
}
public static int kth(int max, int len, int kth) {
// 中间范围还管不管的着!
// 有任何一步,中间位置没命中,左或者右命中了,那以后就都管不着了!
// 但是开始时,肯定是管的着的!
boolean closeToMax = true;
int ans = max / offset[len];
while (--kth > 0) {
max %= offset[len--];
int pick = 0;
if (!closeToMax) {
pick = (kth - 1) / number[len];
ans = ans * 10 + pick;
kth -= pick * number[len];
} else {
int first = max / offset[len];
int left = first * number[len];
if (kth <= left) {
closeToMax = false;
pick = (kth - 1) / number[len];
ans = ans * 10 + pick;
kth -= pick * number[len];
continue;
}
kth -= left;
int mid = number[len - 1] + (max % offset[len]) + 1;
if (kth <= mid) {
ans = ans * 10 + first;
continue;
}
closeToMax = false;
kth -= mid;
len--;
pick = (kth + number[len] - 1) / number[len] + first;
ans = ans * 10 + pick;
kth -= (pick - first - 1) * number[len];
}
}
return ans;
}
}