Sorted Lexical Partition of a Number

Pyth, 34

FNyUz#aYmv:zhdedC,+0N+NlzB)efqSTTY

Try it online here. Notice, this has a time (and space) complexity of O(n). Therefore the test case 12345678901234567890 takes too long in the online compiler. Use the offline one instead (1 minute on my laptop).

This is only my first attempt. There might be some room for improvements.

First some ideas how my algorithm works.

  • I interpret the input as string and not as a number.
  • Then I create all possible subsets of [0, 1, 2, ..., len(n-1)]
  • For each of these subsets (lets take [1, 4, 5]), I split the input string into part using these numbers. [input[0:1], input[1, 4], input[4,5], input[5,len(input)]].
  • Afterwards I try to convert these numbers into strings. There can be two problems. Pyth (or Python) throws an exception for an empty string, and for a string of numbers starting with 0. Therefore I use a try - catch block (actually infinity loop with an immediate break). If converting was successfully, I add the result to a list Y.
  • After I handled all subsets, I filter the list Y for results, that are already sorted and print the last one (the last one has the most groups).

Now the detailed explanation:

                            Implicit: z = input() (z is a String!)
                                      Y = empty list
FNyUz                       for each subset N of [0, 1, 2, ..., len(z)-1]:

     #                         start try-catch block (actually an infinite loop, 
                               but the Python implementation uses a try catch. 

      aY                          append to Y:
                C,+0N+Nlz            zip([0] + N, N + [len(z)])
        m                            map each element d to
          :zhded                     z[d[0]:d[-1]]
         v                           evaluated
                         B        if this didn't throw an exception already, end the infinite loop
                          ) end for loop   

 f    Y      filter Y for elements T, for which
  qSTT           sorted(T) == T
e            and print the last one (the subsets generated with yUz are sorted 
             by length, so the last one has the most groups)

Mathematica, 134 127 bytes

This is pretty inefficient since it generates a lot more partitions than the valid ones. The 324142434445 test case runs within a few seconds, but I wouldn't try 12345678901234567890.

f/@Last@Select[Needs@"Combinatorica`";f=FromDigits;SetPartitions[d=IntegerDigits@#],0<=##&@@f/@#&&Join@@#==d&&#~FreeQ~{0,__}&]&

This defines an unnamed function which takes an integer and returns a list of integers.

Explanation

The reading order of this code is a bit all over the place, so I'll break down in the order it's intended to be read (and evaluated for the most part):

  • d=IntegerDigits@# get the decimal digits of the input and assign this list to d.
  • SetPartitions (which requires Needs@"Combinatorica`";) gives me all partitions of this. However, it returns a lot more than I actually want since it treats the input as a set. This is what makes it inefficient, but I'm using this because the shortest way I know to get all list partitions is much longer. As an example, if the list was {1, 2, 3} the function would return:

    {{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}, {{1}, {2}, {3}}}
    

    Note that a) the consecutive partitions are all in the right order and b) the partitions are sorted from coarsest to finest.

  • Select[...,...&] then filters this list by the anonymous function passed in as the second argument.
    • Join @@ # == d checks that we've actually got a list partition instead of a general set partition.
    • #~FreeQ~{0, __} checks that no partition starts with a leading zero.
    • 0 <= ## & @@ f /@ # is a bit more obscure. First we map FromDigits onto each list in the partition to recover the numbers represented by the digits. Then we apply 0 <= ## to those numbers, where ## refers to all the numbers. If the partition is {1, 23, 45} then this expands to 0 <= 1 <= 23 <= 45, so it checks that the array is sorted.
  • Last@ then gives me the last partition left after filtering - this works because SetPartitions already sorted the partitions, such that the finest ones are at the end.
  • Finally, f/@ recovers the numbers from the digit lists.

Python 3, 134 bytes

def f(s,n=0,L=[],R=[],i=0):
 while s[i:]:i+=1;m=int(s[:i]);R=max([f(s[i:],m,L+[m]),R][m<n or"1">s[i:]>"":],key=len)
 return[L,R][s>""]

It's a little messy, but oh well. The program just generates all valid partitions recursively. The interesting part is that to disallow leading zeroes, all that was necessary was an additional or "1">s[i:]>"" condition.

Takes input like f("12345678901234567890") and returns a list of ints.