Slightly improved version (deals with multiple characters together instead of one at a time):

# Pack.py
def Pack(Words):
    if not Words:
        return ''
    # The method is to build up the result by adding letters at the beginning     # and working forward, and by adding letters at the end, working backwards,
    # meanwhile shrinking the words in the list.
    Words = list(Words) # Don't mutate the original
    Initial = ''
    Final = ''
    while True:
        # It is safe to add an initial letter (of one or more of the words) if
        # EITHER    There is no word that contains it as
        #             a non-initial letter but not as an initial letter.
        #  OR       All words start with it.
        while True:
            FirstLetters = set(w[0] for w in Words)
            FirstLettersSafe = sorted(ch for ch in FirstLetters if
                all(w[0]==ch for w in Words)
                or not any(ch in w[1:] and w[0]!=ch for w in Words))
                # sorted() to make the answer deterministic
                # (not dependent on set ordering)
            if not FirstLettersSafe:
                break
            AnyProgress = True
            Initial += ''.join(FirstLettersSafe)   # Build up the answer from the beginning             Words = [ (w[1:] if w[0] in FirstLettersSafe else w) for w in Words ]
            Words = [ w for w in Words if w != '']
            if not Words:
                return Initial + Final
        # It is safe to add a final letter (of one or more of the words) of
        # EITHER    There is no word that contains it as
        #             a non-final letter but not as a final letter.
        #  OR       All words end with it.
        while True:
            LastLetters = set(w[-1] for w in Words)
            LastLettersSafe = sorted(ch for ch in LastLetters if
                all(w[-1]==ch for w in Words)
                or not any(ch in w[:-1] and w[-1]!=ch for w in Words))
                # sorted() to make the answer deterministic
                # (not dependent on set ordering)
            if not LastLettersSafe:
                break
            Final = ''.join(LastLettersSafe) + Final   # Build up the answer from the end             Words = [ (w[:-1] if w[-1] in LastLettersSafe else w) for w in Words ]
            Words = [ w for w in Words if w != '']
            if not Words:
                return Initial + Final
        if not (FirstLettersSafe or LastLettersSafe):
            break # stuck
    # Try all the possibilities for the next letter to add at the beginning,
    # with recursive calls, and pick one that gives a shortest answer:
    BestResult = None
    for ch in FirstLetters:
            Words2 = list( (w[1:] if w[0] == ch else w) for w in Words )
            Words2 = [ w for w in Words2 if w != '' ]
            res = ch + Pack(Words2)
            if BestResult is None or len(res) < len(BestResult):
                BestResult = res
    return Initial + BestResult + Final

print(Pack(['APPLE', 'PIE', 'APRICOT', 'BANANA', 'CANDY']))

Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to