440: K-th-Smallest-in-Lexicographical-Order
Hard


table of contents

Lowkey this question is hard…

Obviously, a solution that involves computing all the integers in lexicographical order before finding the kth smallest integer will end up TLE-ing.

Now, since having to calculate each individual value in lexicographical order is inefficient (as we do not really care about any value other than the kth smallest integer), it would be stellar if we were able to skip calculating, for example, all the values present between 1 and 2 if we can determine that the kth smallest integer is not between 1 and 2.

In actuality, we can in logarithmic time complexity by computing the number of elements needed to pass to the next consecutive number.

Take for example, n = 110 and k = 64.

Obviously at first glance, it is implied that the 64th smallest integer can not POSSIBLY be between 1 and 2, but how will our algorithm decipher this?! a If we write out the values between 1 and 2 in lexicographical order, we get:

Upon further investigation, realise that you can separate the numbers into certain categories:

After grouping and summing the values together, we calculate that there is 1 + 10 + 11 = 22 numbers needed to pass to go from 1 to 2.

We can repeat this, until we find out that the 64th smallest integers is present between 5 and 6. Then, since the elements between 5 and 6 are:

We can shrink the “borders” by multiplying 5 by a factor of 10 to check if the 64th smallest integers are present between 50 and 51 (and so on and so forth).

This forms the basis of our algorithm.

class Solution {
public:
    int countElementsBetween(long long current, int n) {
        int count = 0;
        long long next = current + 1;
        while (current <= n) {
            count += min((long long) n+1, next)-current;
            current *= 10;
            next *= 10;
        }
        return count;
    }
    int findKthNumber(int n, int k) {
        long long current = 1;
        --k;

        while (k > 0) {
            int currentDifference = countElementsBetween(current, n);
            if (currentDifference <= k) {
                k -= currentDifference;
                ++current;
            } else {
                --k;
                current *= 10;
            }
        }

        return current;
    }
};

code

class Solution {
public:
    int countElementsBetween(long long current, int n) {
        int count = 0;
        long long next = current + 1;
        while (current <= n) {
            count += min((long long) n+1, next)-current;
            current *= 10;
            next *= 10;
        }
        return count;
    }
    int findKthNumber(int n, int k) {
        long long current = 1;
        --k;

        while (k > 0) {
            int currentDifference = countElementsBetween(current, n);
            if (currentDifference <= k) {
                k -= currentDifference;
                ++current;
            } else {
                --k;
                current *= 10;
            }
        }

        return current;
    }
};

complexity