Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Given an integer N, print numbers from 1 to N in lexicographic order

Tags:

c++

algorithm

I'm trying to print the numbers from 1 to N in lexicographic order, but I get a failed output. for the following input 100, I get the 100, but its shifted and it doesn't match with the expected output, there is a bug in my code but I can not retrace it.

class Solution {
public:
    vector<int> lexicalOrder(int n) {
         vector<int> result;
        for(int i = 1; i <= 9; i ++){
        int j = 1;
        while( j <= n){
            for(int m = 0; m < j ; ++ m){
                if(m + j * i <= n){

                    result.push_back(m+j*i);
                }
            }
            j *= 10;
        }
    }
    return result;

    }
};



Input:
100
Output:
[1,10,11,12,13,14,15,16,17,18,19,100,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,61,62,63,64,65,66,67,68,69,7,70,71,72,73,74,75,76,77,78,79,8,80,81,82,83,84,85,86,87,88,89,9,90,91,92,93,94,95,96,97,98,99]

Expected:
[1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47
like image 511
andre Avatar asked Aug 21 '16 01:08

andre


People also ask

What is lexicographical order in integers?

When applied to numbers, lexicographic order is increasing numerical order, i.e. increasing numerical order (numbers read left to right). For example, the permutations of {1,2,3} in lexicographic order are 123, 132, 213, 231, 312, and 321. When applied to subsets, two subsets are ordered by their smallest elements.

How do you calculate lexicographic order?

Approach: Find a string which is lexicographically greater than string S and check if it is smaller than string T, if yes print the string next else print “-1”. To find string, iterate the string S in the reverse order, if the last letter is not 'z', increase the letter by one (to move to next letter).

What is lexicographic order example?

Lexicographical order is nothing but the dictionary order or preferably the order in which words appear in the dictonary. For example, let's take three strings, "short", "shorthand" and "small". In the dictionary, "short" comes before "shorthand" and "shorthand" comes before "small". This is lexicographical order.

How do I print a list in lexicographical order in Python?

Lexicographical order In Python is achieved by using sort() and sorted() function. sort() function sorts data elements in lexicographical order by performing operations on the input list. sorted() function sorts data elements in lexicographical order by replicating the input list and keeping the input list as it is.


2 Answers

Think about when i=1,j=10 what will happen in

for(int m = 0; m < j ; ++ m){
                if(m + j * i <= n){

                    result.push_back(m+j*i);
                }
            }

Yes,result will push_back 10(0+10*1),11(1+10*1),12(2+10*1).. Here is a solution:

#include <iostream>
#include <vector>
#include <string>
std::vector<int> fun(int n)
{
        std::vector<std::string> result;
        for (int i = 1; i <= n; ++i) {
            result.push_back(std::to_string(i));
        }
        std::sort(result.begin(),result.end());
        std::vector<int> ret;
        for (auto i : result) {
            ret.push_back(std::stoi(i));
        }
        return ret;
}
int main(int argc, char *argv[])
{
        std::vector<int> result = fun(100);
        for (auto i : result) {
            std::cout << i << ",";
        }
        std::cout << std::endl;
        return 0;
}
like image 85
icecity96 Avatar answered Oct 26 '22 18:10

icecity96


You are looping through all 2 digit numbers starting with 1 before outputting the first 3 digit number, so your approach won't work.

One way to do this is to output the digits in base 11, padded out with leading spaces to the maximum number of digits, in this case 3. Output 0 as a space, 1 as 0, 2 as 1 etc. Reject any numbers that have any non-trailing spaces in this representation, or are greater than n when interpreted as a base 10 number. It should be possible to jump past multiple rejects at once, but that's an unnecessary optimization. Keep a count of the numbers you have output and stop when it reaches n. This will give you a lexicographical ordering in base 10.

Example implementation that uses O(1) space, where you don't have to generate and sort all the numbers up front before you can output the first one:

void oneToNLexicographical(int n)
{
    if(n < 1) return;

    // count max digits
    int digits = 1, m = n, max_digit11 = 1, max_digit10 = 1;
    while(m >= 10)
    {
        m /= 10; digits++; max_digit11 *= 11; max_digit10 *= 10;
    }

    int count = 0;
    bool found_n = false;
    // count up starting from max_digit * 2 (first valid value with no leading spaces)
    for(int i = max_digit11 * 2; ; i++)
    {
        int val = 0, trailing_spaces = 0;
        int place_val11 = max_digit11, place_val10 = max_digit10;
        // bool valid_spaces = true;
        for(int d = 0; d < digits; d++)
        {
            int base11digit = (i / place_val11) % 11;
            if(base11digit == 0)
            {
                trailing_spaces++;
                val /= 10;
            }
            else
            {   
                // if we got a non-space after a space, it's invalid
                // if(trailing_spaces > 0)
                // {
                //  valid_spaces = false;
                //  break;  // trailing spaces only
                // }
                val += (base11digit - 1) * place_val10;
            }
            place_val11 /= 11;
            place_val10 /= 10;
        }
        // if(valid_spaces && (val <= n))
        {
            cout << val << ", ";
            count++;
        }
        if(val == n)
        {
            found_n = true;
            i += 10 - (i % 11); // skip to next number with one trailing space
        }

        // skip past invalid numbers:

        // if there are multiple trailing spaces then the next run of numbers will have spaces in the middle - invalid
        if(trailing_spaces > 1)
            i += (int)pow(11, trailing_spaces - 1) - 1;
        // if we have already output the max number, then all remaining numbers
        // with the max number of digits will be greater than n
        else if(found_n && (trailing_spaces == 1))
            i += 10;

        if(count == n)
            break;
    }
}

This skips past all invalid numbers, so it's not necessary to test valid_spaces before outputting each.

The inner loop can be removed by doing the base11 -> base 10 conversion using differences, making the algorithm O(N) - the inner while loop tends towards a constant:

int val = max_digit10;
for(int i = max_digit11 * 2; ; i++)
{
    int trailing_spaces = 0, pow11 = 1, pow10 = 1;
    int j = i;
    while((j % 11) == 0)
    {
        trailing_spaces++;
        pow11 *= 11;
        pow10 *= 10;
        j /= 11;
    }

    int output_val = val / pow10;       
    if(output_val <= n)
    {
        cout << output_val << ", ";
        count++;
    }
    if(output_val == n)
        found_n = true;

    if(trailing_spaces > 1)
    {
        i += (pow11 / 11) - 1;
    }
    else if(found_n && (trailing_spaces == 1))
    {
        i += 10;
        val += 10;
    }
    else if(trailing_spaces == 0)  
        val++;

    if(count == n)
        break;
}

Demonstration

The alternative, simpler approach is just to generate N strings from the numbers and sort them.

like image 31
samgak Avatar answered Oct 26 '22 20:10

samgak