text:
Optimizing swiping across a 1D keyboard
This is a code-challenge with a custom scoring system, where the lowest score wins.
Introduction
Many smartphones allow to enter text by swiping your finger across the 2D virtual keyboard. This technology is usually combined with a prediction algorithm that outputs a list of guessed words, sorted from most likely to least likely.
In this challenge:

We are going to swipe across a one-dimensional keyboard restricted to a subset of the 26 letters.
There will be no prediction algorithm: we want each word to be uniquely identified by its 'swipe sequence'.
We want the keyboard to be optimized in such a way that the total number of moves for a given list of words is minimized.

Swiping in one dimension
Below is a lexicographically sorted 1D keyboard with all letters:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
NB: This may be displayed on several rows if you're browsing from mobile. Please think of it as a single row.
To enter the word 'GOLF' on such a keyboard, we will:

start at G
swipe to the right to O
swipe to the left to F

Because L is located between O and F, we just go on swiping without stopping there.
So the swipe sequence of 'GOLF' on this keyboard is GOF.
More generally:

The first and last letters are always included.

Other letters are included if and only if a direction change is required just after them.

Repeated letters must be treated the same way as single letters. For instance, on the above keyboard:

'LOOP' would be encoded as LP (with no stop on O)
'GOOFY' would be encoded as GOFY (the O is included because there's a direction change there -- not because it's doubled)

Keyboard optimization
Let's consider the following list of words: ['PROGRAMMING', 'PUZZLES', 'AND', 'CODE', 'GOLF'].
We need 16 distinct letters to type these words, so we just need a 16-letter keyboard. The following one is -- again -- lexicographically sorted:
ACDEFGILMNOPRSUZ
With this keyboard, the words will be encoded this way:

PROGRAMMING: PRGRAMING (9 moves)
PUZZLES: PZES (4 moves)
AND: AND (3 moves)
CODE: CODE (4 moves)
GOLF: GOF (3 moves)

That's a total of 23 moves for all words.
But we can do much better with this keyboard:
CGODSELZNUIFRPAM
Which gives:

PROGRAMMING: PGMG (4 moves)
PUZZLES: PS (2 moves)
AND: AD (2 moves)
CODE: CE (2 moves)
GOLF: GF (2 moves)

for a total of only 12 moves.
Keyboard scoring
For a given list of \$n\$ words, the score of a keyboard is given by:
$$\sum_{k=1}^{n}{m_k}^2$$
where \$m_k\$ is the number of moves for the \$k\$-th word.
For instance, the two keyboards described in the previous paragraph would be scored \$9^2+4^2+3^2+4^2+3^2=131\$ and \$4^2+2^2+2^2+2^2+2^2=32\$ respectively.
The lower, the better.
The challenge

Given a list of words, your code must output a valid keyboard for this list. A keyboard is considered valid if each word generates a unique swipe sequence.

You'll be given 11 independent lists of words. Your score will be equal to:
$$S + L$$
where \$S\$ is the sum of the scores of your keyboards for all lists and \$L\$ is the length of your code in bytes.
You can use this script to check your score. The score() function expects your code length as first parameter and an array of 11 keyboard strings as second parameter (the case doesn't matter).

The submission with the lowest score wins. In case of a tie, the submission that was submitted first wins.

Additional rules

Your code must be deterministic (i.e. it must always return the same output for a given input).
You must either A) provide a test link (e.g. on TIO) that does not time out, or B) include the generated keyboards within the body of your answer.
You may take the words in either full uppercase or full lowercase. Mixed cases are forbidden.
The input is guaranteed to have at least one solution.
All words are made of at least 2 distinct letters.
Your code must work for any valid input. It will be tested with an undisclosed list of words to make sure that it's not relying on hard-coded results.
I reserve the right to increase the size of the test suite at any time to make sure that the submissions are not optimized for the initial test cases.

Word lists
1) Sanity check #1 (only 4 valid solutions: HES, SEH, ESH or HSE)
SEE, SHE

2) Sanity check #2 (16 valid solutions, of which 4 are optimal: COLD, DOLC, DLOC or CLOD)
COLD, CLOD

3) Sanity check #3
ACCENTS, ACCESS

4) Warm-up
RATIO, NATION, NITRO, RIOT, IOTA, AIR, ART, RAT, TRIO, TRAIN

5) Pangram
THE, QUICK, BROWN, FOX, JUMPS, OVER, LAZY, DOG

6) Common prepositions
TO, OF, IN, FOR, ON, WITH, AT, BY, FROM, UP, ABOUT, INTO, OVER, AFTER

7) Common verbs
BE, HAVE, DO, SAY, GET, MAKE, GO, KNOW, TAKE, SEE, COME, THINK, LOOK, WANT, GIVE, USE, FIND, TELL, ASK, WORK, SEEM, FEEL, TRY, LEAVE, CALL

8) Common adjectives
GOOD, NEW, FIRST, LAST, LONG, GREAT, LITTLE, OWN, OTHER, OLD, RIGHT, BIG, HIGH, DIFFERENT, SMALL, LARGE, NEXT, EARLY, YOUNG, IMPORTANT, FEW, PUBLIC, BAD, SAME, ABLE

9) Common nouns
TIME, PERSON, YEAR, WAY, DAY, THING, MAN, WORLD, LIFE, HAND, PART, CHILD, EYE, WOMAN, PLACE, WORK, WEEK, CASE, POINT, GOVERNMENT, COMPANY, NUMBER, GROUP, PROBLEM, FACT

10) POTUS
ADAMS, ARTHUR, BUCHANAN, BUREN, BUSH, CARTER, CLEVELAND, CLINTON, COOLIDGE, EISENHOWER, FILLMORE, FORD, GARFIELD, GRANT, HARDING, HARRISON, HAYES, HOOVER, JACKSON, JEFFERSON, JOHNSON, KENNEDY, LINCOLN, MADISON, MCKINLEY, MONROE, NIXON, OBAMA, PIERCE, POLK, REAGAN, ROOSEVELT, TAFT, TAYLOR, TRUMAN, TRUMP, TYLER, WASHINGTON, WILSON

11) Transition metals
SCANDIUM, TITANIUM, VANADIUM, CHROMIUM, MANGANESE, IRON, COBALT, NICKEL, COPPER, ZINC, YTTRIUM, ZIRCONIUM, PLATINUM, GOLD, MERCURY, RUTHERFORDIUM, DUBNIUM, SEABORGIUM, BOHRIUM, HASSIUM, MEITNERIUM, UNUNBIUM, NIOBIUM, IRIDIUM, MOLYBDENUM, TECHNETIUM, RUTHENIUM, RHODIUM, PALLADIUM, SILVER, CADMIUM, HAFNIUM, TANTALUM, TUNGSTEN, RHENIUM, OSMIUM

Sandbox (now deleted).

Guess who else knows the struggle...

If you're going to include this rule: "Your code must run in less than 1 minute for each list", it might be good to specify where it will be running. Code that runs on one computer in one minute could take hours on another.

@mypetlion What really matters here is that the code actually outputs something (and not just runs forever), so I've relaxed this rule.

"A keyboard is considered valid if each word generates a unique swipe sequence." - what does unique mean here? for instance, is alphabetic order an invalid solution for the words 'abda','acda'?

@ngn Indeed. These 2 words would have the same swipe sequence ADA, making them undistinguishable. So this keyboard is invalid for this list of words.

Python 3 + Google OR-Tools, 1076 + 1971 = 3047
This always finds optimal solutions (but spends a lot of code to do so).  It runs tests 1–9 in a few seconds, test 10 in six minutes, and test 11 in one minute.
Code
from ortools.sat.python.cp_model import*
from itertools import*
C=combinations
R=range
L=len
T=lambda w:[*zip(w,w[1:],w[2:])]
W=[(*(g[0]for g in groupby(w)),)for w in input().split()]
K={*sum(W,())}
m=CpModel()
V=m.NewBoolVar
B={c:V(f"B{c}")for c in C(K,2)}
for a,b in[*B]:B[b,a]=B[a,b].Not()
for a,b,c in permutations(K,3):m.AddBoolOr([B[a,b],B[b,c],B[c,a]])
M={t:V(f"M{t}")for t in{*sum(map(T,W),[])}}
for a,b,c in M:m.AddBoolXOr([B[a,b],B[b,c],M[a,b,c].Not()])
N={(w,n):V(f"N{w,n}")for w in W for n in R(1,L(w))}
for w in W:
 for n in R(1,L(w)-1):s=sum(M[t]for t in T(w));m.Add(s>=n).OnlyEnforceIf(N[w,n]);m.Add(s<n).OnlyEnforceIf(N[w,n].Not())
for a,b in C(W,2):
 if(a[0],a[-1])==(b[0],b[-1]):m.AddForbiddenAssignments([M[t]for t in T(a)+T(b)],[[x in X for x in R(L(a)-2)]+[y in Y for y in R(L(b)-2)]for n in R(L(a))for X in C(R(L(a)-2),n)for Y in C(R(L(b)-2),n)if[a[x+1]for x in X]==[b[y+1]for y in Y]])
m.Minimize(sum((2*n+3)*N[w,n]for w,n in N))
s=CpSolver()
s.Solve(m)
o={k:0for k in K}
for c in C(K,2):o[c[s.Value(B[c])]]+=1
print(*sorted(K,key=lambda k:o[k]),sep="")

Result

SEH, 13
DOLC, 20
TNSECA, 13
RATION, 80
TYKCIDBRFHJUEVOXWNGZALQMPS, 32
REWINTHUVOFABMPY, 66
FYCWORTMHAGINDKVESULB, 125
TSHRDABXLYOWUPMIENGCF, 213
PVKEFDLBMUSWOIHACNYTRG, 212
XHGTPMCKSUABYORDLJEIWNFV, 596
PYLFNAVEKBOCHTRGDSIZUM, 601

Check score

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

sort: votes


text:
Coat of Many Colours
Challenge
Given a list of unique colour names as input, sort them in the order that they first appear in Joseph's Amazing Technicolour Dreamcoat.

Example
Input:  green, blue, red, brown
Output: red, green, brown, blue

The full list of colours, in order, is:
 1. red
 2. yellow
 3. green
 4. brown
 5. scarlet
 6. black
 7. ochre
 8. peach
 9. ruby
10. olive
11. violet
12. fawn
13. lilac
14. gold
15. chocolate
16. mauve
17. cream
18. crimson
19. silver
20. rose
21. azure
22. lemon
23. russet
24. grey
25. purple
26. white
27. pink
28. orange
29. blue

Or as an array of strings:
["red","yellow","green","brown","scarlet","black","ochre","peach","ruby","olive","violet","fawn","lilac","gold","chocolate","mauve","cream","crimson","silver","rose","azure","lemon","russet","grey","purple","white","pink","orange","blue"]

Rules

You may take input by any reasonable, convenient means (e.g., an array of strings, a delimited string, individual strings) as long as it's permitted by our standard I/O rules, but please specify your input method in your answer.
You may do the same for your output.
The input will only ever contain colours from the above list.
Your solution should be able to handle empty inputs.
You may choose whether all words in the input are consistently uppercase, lowercase or title case but your output's casing must match your input's.
This is code-golf so lowest byte count in each language wins.
As always, standard loopholes are forbidden.

Test cases
Input:  []
Output: []

Input:  ["green", "blue", "red", "brown"]
Output: ["red", "green", "brown", "blue"]

Input:  ["gold", "grey", "green"]
Output: ["green", "gold", "grey"]

Input:  ["ruby","yellow","red","grey"]
Output: ["red", "yellow", "ruby", "grey"]

Input:  ["gold", "green", "fawn", "white", "azure", "rose", "black", "purple", "orange", "silver", "ruby", "blue", "lilac", "crimson", "pink", "cream", "lemon", "russet", "grey", "olive", "violet", "mauve", "chocolate", "yellow", "peach", "brown", "ochre", "scarlet", "red"]
Output: ["red", "yellow", "green", "brown", "scarlet", "black", "ochre", "peach", "ruby", "olive", "violet", "fawn", "lilac", "gold", "chocolate", "mauve", "cream", "crimson", "silver", "rose", "azure", "lemon", "russet", "grey", "purple", "white", "pink", "orange", "blue"]

Sandbox (hard to believe it'd been languishing there for 18 months!)

PowerShell, 262 155 151 127 125 95 bytes
$args|sort{"rlyegwbrscbrocpyrvo lvnfaldgccvmacmcvseraolsrygpptwkpnoeb".indexof((-join$_[3,0]))}

Try it online!
Naive approach. PowerShell sort-object can sort based on a script block that gets executed for every object. Here we're simply getting the .IndexOf() the color from a string, which will assign a numerical value to each color, and then sorts based on those numbers. The string is constructed from the fourth and first letters of each color to ensure uniqueness. Output is implicit.
-4 bytes thanks to Shaggy.
-2 bytes thanks to mazzy.
A whopping -30 bytes thanks to KGlasier.

I don't know if you could do this efficiently (byte-wise), but if you sort by length 3 substrings and then sort with secondary key of the original string, the only collision is green grey which is in the right alphabetical order.

@Shaggy Yes, that works because .IndexOf() will return -1 if the string isn't found, which sorts red into the right ordering. Thanks!

i think you can remove brackets around a string.

@mazzy Indeed, thanks!

Hey, I think I found a shorter string... check out this version! If you sort the string by the fourth and the first indexes of each color and search with this string: rlyegwbrscbrocpyrvo lvnfaldgccvmacmcvserraolsrygpptwkpnoeb (has a space after the o to deal with a duplicate issue) you save like 27 characters. If you can find an issue with it let me know.

Removed an r... rlyegwbrscbrocpyrvo lvnfaldgccvmacmcvseraolsrygpptwkpnoeb. Each color is indexed once, with exception of red, which matches 6 times. But one of the matches is the first, so it still works. I'd link a https://tio.run/ but the link is too long.

@KGlasier Wow, thanks for finding that string! That saves a lot of bytes.

@AdmBorkBork According to your bio this should now be your most golfed answer! Cut 63% or 167 bytes from the original. Higher than your 24% cut or 90 bytes on Help me Manage my Time

@KGlasier Indeed. I've updated my bio accordingly.

JavaScript (SpiderMonkey),  104  101 bytes
"When in doubt, just hash the bloody input."
a=>a.sort((a,b)=>(g=s=>'mgo0dakbrfs0h0000j9c412603e870500p0l0niq'[parseInt(s,36)%657%53%42])(a)>g(b))

Try it online!

I've seen (and been impressed by) hash-based solutions like this before.  Is there an explanation anywhere for how the magic string/multiplier/mod values are generated?  Is it just brute force until you find a set of values that gives a unique output for each of the possible color inputs, or is there a more clever approach?

@JackBrounstein This one was just a quick and dirty brute force search trying random values and minimizing only the maximum output (after the last modulo), without taking the length of the full chain into account (e.g. %99%55 is not better than %123%55 with this approach). So it's certainly sub-optimal. I may try something slightly more sophisticated later, though.

Jelly, 28 bytes
“½Ṗ©cƘʂẒẹMMỤẓHP’Œ?“ðÑþQ’,ḥµÞ

Try it online!
How it works
µ turns everything to its left into a monadic chain, which Þ maps over the input array and sorts the input according to the generated values.
“½Ṗ©cƘʂẒẹMMỤẓHP’ sets the return value to 176073885534954276199526358143331.
Œ? generates the 176073885534954276199526358143331th permutation of the positive integers (without the sorted tail), yielding \$\small[20,28,15,3,5,26,18,16,8,30,4,25,2,21,22,11,24,1,23,10,29,12,17,27,14,9,6,13,7,19]\$.
“ðÑþQ’ yields 391695582; , prepends it to the permutation. Then, ḥc ompute Jelly's 391695582th hash function, mapping the
                                  resulting buckets to the integers of the permutation.
The magic constant 391695582 was found by Jelly's utils.
dennis-home:utils$ time ./findhash 30 29 <<< '["red","yellow","green","brown","scarlet","black","ochre","peach","ruby","olive","violet","fawn","lilac","gold","chocolate","mauve","cream","crimson","silver","rose","azure","lemon","russet","grey","purple","white","pink","orange","blue"]'
391695582

real    0m2.058s
user    0m15.077s
sys     0m0.023s

353690280752 hashes the 29 colors in 29 buckets, but takes one more byte to encode. Using uppercase (332849952364) or titlecase (862442225888) also comes out at 28 bytes.

Python 3, 93 bytes
lambda r:sorted(r,key=lambda s:'iV^ZzwnFM@pYuOobXGAKyf[tUR]E'.find(chr(int(s,36)%127%60+64)))

Try it online!
Reads each color as a base-36 int. Brute-forced the moduli and chose an arbitrary offset among the 19 that didn't require escapes.

Powershell, 124 120 124 119 118 102 bytes
$args|sort{$c=$_
'bluOrPiWPuG*yRusLeARoSiCriCrMCGoLFVOlRuPOBlSBGYR'-csplit'(?=[A-Z])'|%{$c-like"$_*"}}

Try It Online!
Explanation:

The data string contains the first significant letters of the color labels in descending order. Except for the Grey label - G*y is shorter.
-csplit'(?=[A-Z])' splits the data string to the array (blu,Or,Pi,W,Pu,G*y,Rus,Le,A,Ro,Si,Cri,Cr,M,C,Go,L,F,V,Ol,Ru,P,O,Bl,S,B,G,Y,R)
|%{$c-like"$_*"} maps the string array to the array of boolean. Where True means "a color label starts from this string" (like is case-insensitive operator, csplit - case-sensitive. see doc).
sort{} sorts a color lables by the arrays of boolean in ascending order.

The sorting by array is a very interesting feature in the Powershell. In this script all arrays have the same length and contain Boolean values only. This sorting is performed in the lexographic order of boolean arrays.
Therefore, the string can contain one-letter abbreviations for the last labels. If there is a match at the beginning of the array, the matches at the end have no effect.
       blu Or Pi W Pu G*y Rus Le A Ro Si Cri Cr M C Go L F V Ol Ru P O Bl S B G Y R
green: -   -  -  - -  -   -   -  - -  -  -   -  - - -  - - - -  -  - - -  - - T - -
gold : -   -  -  - -  -   -   -  - -  -  -   -  - - T  - - - -  -  - - -  - - T - -
grey : -   -  -  - -  T   -   -  - -  -  -   -  - - -  - - - -  -  - - -  - - T - -

     : green < gold < grey

Where T is true and - is false.

Test script:
$f = {

$args|sort{$c=$_
'bluOrPiWPuG*yRusLeARoSiCriCrMCGoLFVOlRuPOBlSBGYR'-csplit'(?=[A-Z])'|%{$c-like"$_*"}}

}

@(
    ,( @(), @() )
    ,( ('green', 'blue', 'red', 'brown'), ('red', 'green', 'brown', 'blue') )
    ,( ("gold", "grey", "green"), ("green", "gold", "grey") )
    ,( ("ruby","yellow","red","grey"), ("red", "yellow", "ruby", "grey") )
    ,( ("gold", "green", "fawn", "white", "azure", "rose", "black", "purple", "orange", "silver", "ruby", "blue", "lilac", "crimson", "pink", "cream", "lemon", "russet", "grey", "olive", "violet", "mauve", "chocolate", "yellow", "peach", "brown", "ochre", "scarlet", "red"),
       ("red", "yellow", "green", "brown", "scarlet", "black", "ochre", "peach", "ruby", "olive", "violet", "fawn", "lilac", "gold", "chocolate", "mauve", "cream", "crimson", "silver", "rose", "azure", "lemon", "russet", "grey", "purple", "white", "pink", "orange", "blue") )
) | % {
    $inp,$expected = $_
    $result = &$f @inp  # splatting
    "$("$result"-eq"$expected"): $result"
}

Output:
True:
True: red green brown blue
True: green gold grey
True: red yellow ruby grey
True: red yellow green brown scarlet black ochre peach ruby olive violet fawn lilac gold chocolate mauve cream crimson silver rose azure lemon
russet grey purple white pink orange blue

I have This site can’t be reached error. Sorry.

Added a revised TIO for you.

I've managed to get a new IP for TIO today. Is it still blocked for you?

It's Alive!!! Cool! And Thanks!

C (clang), 121 119 bytes
#define j(i)index(".MH^>SALcD8Z!)3TF(RNe*aQU<,'",(**i^377)%75+30)
s(**a,**b){return j(a)-j(b);}f(*t,n){qsort(t,n,8,s);}

Try it online!
-2 bytes thanks ceilingcat!

Wolfram Language 255 213 199 bytes
Fourteen bytes saved by Dennis, who avoided the " marks, using symbols instead of strings.
SortBy[#,{yellow,green,brown,scarlet,black,ochre,peach,ruby,olive,violet,fawn,lilac,gold,chocolate,mauve,cream,crimson,silver,rose,azure,lemon,russet,grey,purple,white,pink,orange,blue}~Position~#&]&

Try It Online!

I will improve the string compression in a while
Japt, 88 78 71 bytes
ñ@`äŠâwrÒ.cÖ¨acru½ivo¤faØngoÒqauvœamsolv€osz¨e¶s gœrpl–tpˆ„g½u`bXé4 ¯3

Try it online!

A quick port of one of the other solutions here comes in at 46 bytes, if you want to try for it.

@Shaggy I don't think they're trying for it anymore :P

Python 2, 186 bytes
lambda a:[y for x,y in sorted((max(" y gree br sc bla oc pe rub ol v f li go ch m cre cri si ro a le rus grey pu w pi or blu ".find(" %s "%c[:i+1])for i,j in enumerate(c)),c)for c in a)]

Try it online!
Finds all matches for progressive character substrings (Ex: "green" will check for "g", "gr", "gre", "gree", and "green") in the identifier string, and keeps the maximum index. "red" is always first, anf find() returns -1 for missing matches, so there is no identifier for red specifically.
Once the colors are turned into (index, color) pairs, sorts array on first item of pair and then discards first item of each pair.

Python 3, 130 bytes
lambda*a:sorted(a,key=lambda c:("r,ylgebwsrtbcorpcryovvlfnlagdccamvca cmnsvrearlorsgyppwtpkonbe".find(c[::3]+" "*(c=="cream")),c))

Try it online!

@Shaggy I don't think so? can you give me an input and the intended output

@AdmBorkBork Thanks, just realized what Shaggy meant by that. I was only comparing green and grey to each other lol

To add to Shaggy and AdmBorkBork's comments, "green" and "grey" both begin with "gre".

@Shaggy fixed i think

@Shaggy Fixed it, thanks. Required a bit of a hack for cream since ca is a substring of cca for chocolate oof.

Charcoal, 69 68 63 56 52 bytes
Ｆ⪪”&⌊Oτe¿k⟲ⅈζLbψ#>φuＣ№ς…Ｘ◧9gF→G⌈Ｈ≡M*e{-”²Φθ⁼ι⁺§κ⁰§κ³

Try it online! Link is to verbose version of code. Explanation: Now a port of @Sok's Pyth answer.
Ｆ⪪”&⌊Oτe¿k⟲ⅈζLbψ#>φuＣ№ς…Ｘ◧9gF→G⌈Ｈ≡M*e{-”²

Take the compressed string rrylgebwsrbcorpcryovvlfnlagdccmvcacmsvrearlorsgyppwtpkonbe and loop over each substring of length 2.
Φθ⁼ι⁺§κ⁰§κ³

For each substring print those input strings whose first and fourth (cyclically) characters equal the substring.

Would it help to take inputs in Title case? Then Li would be a unique substring for Lilac and you wouldn't need the 99 hack. I don't know if including uppercase letters would make the compressed string longer, though.

@DLosc It would make the compressed string longer, but overall the saving would be 2 bytes.

@DLosc Which turns out to be not as good as simply porting a later answer, unfortunately.

C# (Visual C# Interactive Compiler), 321 219 210 161 159 138 bytes
n=>n.OrderBy(a=>a!="grey"?"yelgrebroscablaochperuboliviofawlilgolchomaucrecrisilrosazulemruspurwhipinorablu".IndexOf(a.Substring(0,3)):65)

Try it online!
-3 bytes thanks to Shaggy, -18 thanks to TheLethalCoder
Takes input as a List<string>, returns an IOrderedEnumerable<string>
How this works is that it orders the list by each string's index in the original string. The original string has every color except for grey turned to it's first three letters. Grey is not there, since green and grey would cause ambiguity. Red isn't there either, since IndexOf returns -1 if the string doesn't appear.
Shorter version taking IOrderedEnumerable as input, 137 bytes
n=>n.ThenBy(a=>a!="grey"?"yelgrebroscablaochperuboliviofawlilgolchomaucrecrisilrosazulemruspurwhipinorablu".IndexOf(a.Substring(0,3)):65)

Takes advantage of the fact that ThenBy is 1 byte shorter than OrderBy, but ThenBy only operates on IOrderedEnumerables though.
Try it online!

I think you can remove red from your lookup string and replace 68 with 65 to save 3 bytes. On my phone so haven't fully tested it.

You can use the string inline with an implicit return for 142 bytes n=>n.OrderBy(a=>a!="grey"?"redyelgrebroscablaochperuboliviofawlilgolchomaucrecrisilrosazulemruspurwhipinorablu".IndexOf(a.Substring(0,3)):68); However, you need to include the usings in your byte count as they are required to run. You can use the namespace trick to shorten the count required though.

If I use the namespace trick, do I have to add the namespace to my number of bytes?

I can just change the compiler to Visual C# Interactive Compiler, and then I won't have to add the usings

Ah good idea on changing to Interactive but yes if you did the namespace trick you'd have to include it in your byte count. Essentially adding namespace System.Linq{} or whatever you would have chosen.

Pyth, 66 bytes
oxc."ayÇæ£ðÐ¥~@iF[2BÍÐ:Yë)^ksTTã"2s@LN,03

Try it online here, or verify all the test cases at once here.
The colours in the list can be uniquely identified by taking the characters at index 0 and 3, assuming modular indexing. This results in the following mapping:
rr -> red
yl -> yellow
ge -> green
bw -> brown
sr -> scarlet
bc -> black
or -> ochre
pc -> peach
ry -> ruby
ov -> olive
vl -> violet
fn -> fawn
la -> lilac
gd -> gold
cc -> chocolate
mv -> mauve
ca -> cream
cm -> crimson
sv -> silver
re -> rose
ar -> azure
lo -> lemon
rs -> russet
gy -> grey
pp -> purple
wt -> white
pk -> pink
on -> orange
be -> blue

Full explanation:
oxc."..."2s@LN,03Q   Implicit: Q=eval(input())
                     Trailing Q inferred, dictionary string replaced with ... for brevity
o                Q   Order the elements of Q, as N, using:
              ,03      [0,3]
           @LN         Get the characters at the above indices in N
          s            Concatenate into a string
                         The above is result {1}
   ."..."              The compressed dictionary string
  c      2             Split into chunks of length 2
 x                     Get the index of {1} in the above
                       Implicit print of sorted list

05AB1E, 48 bytes
Σ.•Aå₂мÕh∊þèmvƶ\kΛ1YŠíJ>J#θ₁2©€,Ù{η¦ù-•2ôy¬s3è«k

Same solution as most other answers. Will try to golf it down from here later.
Try it online or verify all test cases.
Explanation:
Σ          # Sort the (implicit) input-list by:
 .•Aå₂мÕh∊þèmvƶ\kΛ1YŠíJ>J#θ₁2©€,Ù{η¦ù-•
           #  Push compressed string "rrylgebwsrbcorpcryovvlfnlagdccmvcacmsvrearlorsgyppwtpkonbe"
  2ô       #  Split into parts of size 2
 y         #  Push the current string of the list we're sorting
  ¬        #  Push its head (without popping)
   s       #  Swap so the string is at the top of the stack again
    3è     #  Get the character at index 3 (with automatic wraparound)
      «    #  Merge both characters together
       k   #  And get the index in the compressed string to sort on

See this 05AB1E tip (section How to compress strings not part of the dictionary?) to understand how .•Aå₂мÕh∊þèmvƶ\kΛ1YŠíJ>J#θ₁2©€,Ù{η¦ù-• is "rrylgebwsrbcorpcryovvlfnlagdccmvcacmsvrearlorsgyppwtpkonbe".

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
What will be on the top of this decorative calendar?
Someone gave my wife a decorative calendar consisting of four cubes. Here it is showing today's date (as of the posting of this challenge) on the front:

When I first saw it, I looked at it from the wrong angle (from directly above) and couldn't figure out why it gave this information:
[["February", "January"], [3], [7], ["Monday", "Tuesday"]]

Your job is to replicate my error for any date in 2019.
Challenge
Write a program or function that takes any date from 2019, and outputs what appears on the top of all the cubes when that date is displayed facing out from the front of the calendar.
Here are all six sides for all the cubes. To display a 6 you just turn the 9 upside down. The 0 is vertically symmetrical, so 0 upside down is still 0. There might be more than one correct answer for some dates (e.g. any 11th of any month will have more than one way to use the cubes, and the 0 thing) so you can output any correct answer.

Rules

Standard loopholes forbidden.
Input/output format is flexible.
The output does have to be in order by cube, but not within a cube. The order must be month cube first, then the two number cubes, followed by the weekday cube. But when a cube has two elements on top, those two elements can be in either order.
You can replace January to December 0-11 or 1-12 if you like.
You can replace the days of the week with 0-6 or 1-7 if you like, and you can start the week on either Sunday or Monday (but you can't start the week on any other day - this is PPGC, not some sort of crazy-town.)
This is code-colf. Fewest bytes for each language wins.
Explanations encouraged.

Test cases
(Tue) 2019-01-29   [[ "July", "August" ], [3], [7], [ "Thursday", "Wednesday" ]]
                   [[ "August", "July" ], [3], [7], [ "Wednesday", "Thursday" ]]

                   etc. since the order within each cube doesn't matter.

(Thu) 2019-07-11   [[ "May", "June" ], [3], [8], [ "Saturday", "Friday" ]]
                   [[ "May", "June" ], [8], [3], [ "Saturday", "Friday" ]]

                   since the two 1 cubes could be either way.

(Sun) 2019-10-27   [[ "January", "February" ], [3], [6], [ "Friday", "Saturday" ]]

(Wed) 2019-05-01   [[ "March", "April" ], [8], [3], [ "Monday", "Tuesday" ]]
                   [[ "March", "April" ], [6], [3], [ "Monday", "Tuesday" ]]
                   [[ "March", "April" ], [9], [3], [ "Monday", "Tuesday" ]]

                   since the 0 cube could have either the 8 side or the 6 side facing up, and the 6 could also be considered a 9.

(Sat) 2019-08-24   [[ "February", "January" ], [8], [5], [ "Sunday" ]]

So... I've been wondering, how is this decorative thing able to show all dates?

@ErikTheOutgolfer which date is missing?

Not related to the challenge, but, since I can't ping you over chat, how do you use the two middle cubes? I mean, all 10 digits should be able to be represented.

The cubes can be used in either order. Let's call the two numeric cubes Top Right and Bottom Left as in the graphic. To get 18 you use 1 from Top Right and 8 from Bottom Left. To get 13 you use 1 from Bottom Left and 3 from Top Right. And so on. 0 1 and 2 have to be on both cubes. The fact that 6 and 9 share the same cube face gives you everything from 01 to 31 which is all that is needed.

According to the pictures, 27 in 2019-10-27 should go to 36, not 32.

C (glibc), 327 319 286 bytes
#define C strftime(s
#define U(B,E)S[6]=E-1,C+B,99,"%A"+2*!E,S)
S[9],s[99];f(M,D){S[4]=112233107696>>3*M&7;C,9,"%B",S);S[4]^=1;
C+3,9,"%B",S);M=161102>>(D+M*23/9-1-2*(M>2))%7*3&7;U(6,M);U(9,(M^1));
printf("%s/%s %d%d %s/%s\n",
s,s+3,D>29?4:D%10<6?8:3,D>29?8:1070160091>>D%10*3&7,s+6,s+9);}

(Some line breaks added for clarity)
f takes a month (1–12) and a day (1–31). Prints to stdout. Try it online!
Test cases:
2019-01-29: July/August 37 Thursday/Wednesday
2019-07-11: May/June 83 Saturday/Friday
2019-10-27: January/February 36 Friday/Saturday
2019-05-01: March/April 83 Monday/Tuesday
2019-08-24: February/January 85 /Sunday

Ungolfed
#include <stdio.h>
#include <time.h>
void f(int M, int D) {
    int month_cube[] = {6,3,0,5,2,7,4,1,4,0,5,1};
    int day_cube[] = {3,3,3,4,5,2,2,6,7,7};
    int week_cube[] = {6,1,5,2,7,4,0}; /* 1=Sun, 7=Sat, 0=none */
    int D1 = D/10, D2 = D%10;
    char s[4][99] = {{0}};
    struct tm t;

    t.tm_mon = month_cube[M-1];
    strftime(s[0], 99, "%B", &t);
    t.tm_mon = month_cube[M-1]^1;
    strftime(s[1], 99, "%B", &t);

    if (D1 >= 3) { /* D = 30, 31 */
        D1 = 4, D2 = 8;
    } else {
        if (D2 <= 5) {
            D1 = 8; /* 012[69]78: 012 -> 8 */
        } else {
            D1 = 3; /* 012345: 012 -> 3 */
        }
        D2 = day_cube[D2];
    }

    int W = (D + M*23/9 - 1 - 2*(M>2)) % 7; /* day of week */
    if (week_cube[W]) {
        t.tm_wday = week_cube[W] - 1;
        strftime(s[2], 99, "%A", &t);
    }
    if (week_cube[W]^1) {
        t.tm_wday = (week_cube[W]^1) - 1;
        strftime(s[3], 99, "%A", &t);
    }

    printf("%s/%s %d%d %s/%s\n", s[0], s[1], D1, D2, s[2], s[3]);
}

Digit cubes
These are the possibilities for the digits:
Cube 1:
0 1 2 3 4 5
3 3 3 4 5 2
5

Cube 2:
0 1 2        6 7 8 9
8 8 8        2 6 7 7
6                2
9

The following mapping seems like the best choice for golfing:
   01 02 03 04 05 06 07 08 09
   83 83 84 85 82 32 36 37 37

10 11 12 13 14 15 16 17 18 19
83 83 83 84 85 82 32 36 37 37

20 21 22 23 24 25 26 27 28 29
83 83 83 84 85 82 32 36 37 37

30 31
48 48

Cheats
strftime is meant to be called with a struct tm as input. Instead, I declare int S[9] and use S[4] as tm_mon and S[6] as tm_wday. This works if the C library uses the same list of struct members as the ISO standard.
s[99] is used to store various strings from strftime, but making it an int array saves a few bytes in indexing.

JavaScript (ES6), 142 bytes
Takes input as (year, month, day0, day1) where \$month\$ is 1-indexed, \$day0\$ is the first digit of the date and \$day1\$ is the second digit.
Returns (month0, month1, day0, day1, weekDay0, weekDay1) where week days are 0-indexed with \$0\$ for Sunday. If the second week day is blank, it is set to \$-1\$.
(y,m,t,u)=>[M=-~((s='45226276204264')[m--+6]||4*m%2),M+1,t<3?u<6?8:3:s[t-3],u<3?3:s[u-3],D=s[(new Date(y,m,t*10+u).getDay()+6)%7+7],-~D%7||-1]

Try it online!


text:
Parse the Bookworm dictionary format
I've recently been indulging myself in some nostalgia
in the form of Bookworm Deluxe:

In case you haven't seen it before,
it's a word game where the goal is to connect adjacent tiles to form words.
In order to determine whether a string is a valid word,
it checks it against its internal dictionary,
which is stored in a compressed format that looks like this:
aa
2h
3ed
ing
s
2l
3iis
s
2rdvark
8s
4wolf
7ves

The rules for unpacking the dictionary are simple:

Read the number at the start of the line,
and copy that many characters from the beginning of the previous word.
(If there is no number,
copy as many characters as you did last time.)
Append the following letters to the word.

So, our first word is aa,
followed by 2h,
which means "copy the first two letters of aa and append h,"
forming aah.
Then 3ed becomes aahed,
and since the next line doesn't have a number,
we copy 3 characters again to form aahing.
This process continues throughout the rest of the dictionary.
The resulting words from the small sample input are:
aa
aah
aahed
aahing
aahs
aal
aaliis
aals
aardvark
aardvarks
aardwolf
aardwolves

Your challenge is to perform this unpacking
in as few bytes as possible.
Each line of input will contain zero or more digits 0-9
followed by one or more lowercase letters a-z.
You may take input and give output
as either a list of strings,
or as a single string with words separated by any character other than 0-9/a-z.
Here is another small test case
with a few edge cases not covered in the example:
abc cba 1de fg hi 0jkl mno abcdefghijk 10l
=> abc cba cde cfg chi jkl mno abcdefghijk abcdefghijl

You may also test your code on the full dictionary:
input,
output.

Is there a possibility that there will not be a number in the second line? Also, can we assume that no number except 0 will have leading 0s?

@EriktheOutgolfer Yes, that is possible; I've added that to the test case. And yes, you can assume that (as well as that the number won't be greater than the length of the previous word).

That's a cute compression format :]

The locate program uses this type of encoding on pathnames.

I wrote this program for my actual use, about 15 years ago. Unfortunately I don't think I have the source anymore...

If I remembered correctly, I must find this file out from the game about 10 years ago, when I want to win the game more easily. But I did not understand how the compression works. I should read this post earlier. :(

Can we assume the leading number can only be 1 digit long at most?

@ASCII-only There is a counterexample in the test cases.

@Doorknob oh :/ didn't see that

Vim, 57 bytes
:%s/\a/ &
:%norm +hkyiwjP
:g/\d/norm diw-@"yl+P
:%s/ //g

Try it online!

Would <H<G instead of the last substitution work?

@cowsquack Unfortunately, no. Every input that doesn't start with a number increases the number of leading spaces so there's no way to guarantee a < solution would unindent enough times.

I think you can do :%s/ * instead of the last substitution to save two bytes.

JavaScript (ES6),  66 62  61 bytes
a=>a.map(p=s=>a=a.slice([,x,y]=/(\d*)(.*)/.exec(s),p=x||p)+y)

Try it online!
Commented
a =>                  // a[] = input, re-used to store the previous word
  a.map(p =           // initialize p to a non-numeric value
  s =>                // for each string s in a[]:
    a =               //   update a:
      a.slice(        //     extract the correct prefix from the previous word:
        [, x, y] =    //       load into x and y:
          /(\d*)(.*)/ //         the result of a regular expression which splits the new
          .exec(s),   //         entry into x = leading digits and y = trailing letters
                      //       this array is interpreted as 0 by slice()
        p = x || p    //       update p to x if x is not an empty string; otherwise leave
                      //       it unchanged; use this as the 2nd parameter of slice()
      )               //     end of slice()
      + y             //     append the new suffix
  )                   // end of map()

brainfuck, 201 bytes
,[[[-<+>>>+<<]>-[---<+>]<[[-<]>>]<[-]>>[<<,>>>[-[-<++++++++++>]]++++<[->+<]-[----->-<]<]<]>>>[[>>]+[-<<]>>[[>>]+[<<]>>-]]+[>>]<[-]<[<<]>[->[>>]<+<[<<]>]>[>.>]+[>[-]<,.[->+>+<<]>>----------]<[<<]>-<<<,]

Try it online!
Requires a trailing newline at the end of the input.  A version without this requirement is 6 bytes longer:
brainfuck, 207 bytes
,[[[-<+>>>+<<]>-[---<+>]<[[-<]>>]<[-]>>[<<,>>>[-[-<++++++++++>]]++++<[->+<]-[----->-<]<]<]>>>[[>>]+[-<<]>>[[>>]+[<<]>>-]]+[>>]<[-]<[<<]>[->[>>]<+<[<<]>]>[>.>]+[>[-]<,[->+>+<<]>>[----------<.<]>>]<[<<]>-<<<,]

Try it online!
Both versions assume all numbers are strictly less than 255.
Explanation
The tape is laid out as follows:
tempinputcopy 85 0 inputcopy number 1 a 1 a 1 r 1 d 0 w 0 o 0 l 0 f 0 ...

The "number" cell is equal to 0 if no digits are input, and n+1 if the number n is input.  Input is taken at the cell marked "85".
,[                     take input and start main loop
 [                     start number input loop
  [-<+>>>+<<]          copy input to tempinputcopy and inputcopy
  >-[---<+>]           put the number 85 in the cell where input was taken
  <[[-<]>>]            test whether input is less than 85; ending position depends on result of comparison
                       (note that digits are 48 through 57 while letters are 97 through 122)
  <[-]>                clean up by zeroing out the cell that didn't already become zero
  >[                   if input was a digit:
   <<,>>               get next input character
   >[-[-<++++++++++>]] multiply current value by 10 and add to current input
   ++++                set number cell to 4 (as part of subtracting 47)
   <[->+<]             add input plus 10*number back to number cell
   -[----->-<]         subtract 51
  <]                   move to cell we would be at if input were a letter
 <]                    move to input cell; this is occupied iff input was a digit

                       part 2: update/output word

 >>>                   move to number cell
 [                     if occupied (number was input):
  [>>]+[-<<]>>         remove existing marker 1s and decrement number cell to true value
  [[>>]+[<<]>>-]       create the correct amount of marker 1s
 ]
 +[>>]<[-]             zero out cell containing next letter from previous word
 <[<<]>                return to inputcopy
 [->[>>]<+<[<<]>]      move input copy to next letter cell
 >[>.>]                output word so far
 +[                    do until newline is read:
  >[-]<                zero out letter cell
  ,.                   input and output next letter or newline
  [->+>+<<]            copy to letter cell and following cell
  >>----------         subtract 10 to compare to newline
 ]
 <[<<]>-               zero out number cell (which was 1 to make copy loop shorter)
 <<<,                  return to input cell and take input
]                      repeat until end of input

Perl 6, 50 48 bytes
-2 bytes thanks to nwellnhof
{my$l;.map:{$!=S[\d*]=substr $!,0,$l [R||]=~$/}}

Try it online!
A port of Arnauld's solution. Man, that R|| trick was a rollercoaster from 'I think this could be possible', to 'nah, it's impossible', to 'kinda maybe possible' and finally 'aha!'
Explanation:
{my$l;.map:{$!=S[\d*]=substr $!,0,$l [R||]=~$/}}
{                                              }  # Anonymous code block
 my$l;    # Declare the variable $l, which is used for the previous number
      .map:{                                  }  # Map the input list to
            $!=              # $! is used to save the previous word
               S[\d*]=       # Substitute the number for
                      substr $!,0    # A substring of the previous word
                                 ,              # With the length of
                                           ~$0     # The num if it exists
                                  $l [R||]=        # Otherwise the previous num

The $l [R||]=~$/ part roughly translates to $l= ~$/||+$l but... it has the same amount of bytes :(. Originally, it saved bytes using an anonymous variable so the my$l was gone but that doesn't work since the scope is now the substitution, not the map codeblock. Oh well. Anyways, R is the reverse metaoperator, so it reverses the arguments of ||, so the $l variable ends up being assigned the new number (~$/) if it exists, otherwise itself again.
It could be 47 bytes if Perl 6 didn't throw a kinda redundant compiler error for =~.

Ruby, 49 45 43 bytes
$0=$_=$0[/.{0#{p=$_[/\d+/]||p}}/]+$_[/\D+/]

Try it online!
Explanation
$0=                                         #Previous word, assign the value of
   $_=                                      #Current word, assign the value of
      $0[/.{0#{              }}/]           #Starting substring of $0 of length p which is
               p=$_[/\d+/]||p               #defined as a number in the start of $_ if any
                                 +$_[/\D+/] #Plus any remaining non-digits in $_

C (gcc), 65 57 55 53 bytes
n;b[9];c=b;f(){scanf("%d",&n);gets(c+n)&&f(puts(c));}

Try it online!
Explanation:
n;                  /* `n` stores the shared prefix length, implicitly 0 */

b[9];               /* int[] buffer we read into, we need 12+1 bytes for the
                       largest words, this buffer is 9*4=36 bytes */

c=b;                /* if we use `b` in pointer arithmetic it would be an int*,
                       but we want to address individual bytes, so we need to
                       store the address as an integer. */

f() {               /* the unpacking function */

    scanf("%d",&n); /* read the common prefix length, if scanf fails because no
                       new prefix length is given then `n`s value will remain
                       the same, which is exactly what we want!  :-) */

    gets(c+n)       /* read the suffix part, the first `n` characters are the
                       common prefix */

        &&          /* short-circuit, so if the gets fails the next part is not
                       executed */

    f(puts(c));     /* print the word that was extracted and call `f`
                       recursively to handle the next word */
}

Python 3.6+, 172 195 156 123 122 121 104 bytes
import re
def f(l,n=0,w=""):
 for s in l:t=re.match("\d*",s)[0];n=int(t or n);w=w[:n]+s[len(t):];yield w

-12 bytes thanks to Bubbler
-5 bytes thanks to Dennis

Try it online!
Explanation
I caved, and used Regular Expressions. This saved at least 17 bytes. :
t=re.match("\d*",s)[0]

When the string doesn't begin with a digit at all, the length of this string will be 0. This means that:
n=int(t or n)

will be n if t is empty, and int(t) otherwise.
w=w[:n]+s[len(t):]

removes the number that the regular expression found from s (if there's no number found, it'll remove 0 characters, leaving s untruncated) and replaces all but the first n characters of the previous word with the current word fragment; and:
yield w

outputs the current word.

Haskell, 82 81 bytes
tail.map concat.scanl p["",""]
p[n,l]a|[(i,r)]<-reads a=[take i$n++l,r]|1<2=[n,a]

Takes and returns a list of strings.
Try it online!
        scanl p["",""]        -- fold function 'p' into the input list starting with
                              -- a list of two empty strings and collect the
                              -- intermediate results in a list
  p [n,l] a                   -- 1st string of the list 'n' is the part taken form the last word
                              -- 2nd string of the list 'l' is the part from the current line
                              -- 'a' is the code from the next line
     |[(i,r)]<-reads a        -- if 'a' can be parsed as an integer 'i' and a string 'r'
       =[take i$n++l,r]       -- go on with the first 'i' chars from the last line (-> 'n' and 'l' concatenated) and the new ending 'r'
     |1<2                     -- if parsing is not possible
       =[n,a]                 -- go on with the previous beginning of the word 'n' and the new end 'a'
                              -- e.g. [         "aa",     "2h",      "3ed",       "ing"       ]
                              -- ->   [["",""],["","aa"],["aa","h"],["aah","ed"],["aah","ing"]]
  map concat                  -- concatenate each sublist
tail                          -- drop first element. 'scanl' saves the initial value in the list of intermediate results.

Edit: -1 byte thanks to @Nitrodon.

Contrary to usual Haskell golfing wisdom, you can actually save one byte here by not defining the helper function as an infix operator.

@Nitrodon: well spotted! Thanks!

Japt, 19 18 17 bytes
Initially inspired by Arnauld's JS solution.
;£=¯V=XkB ªV +XoB

Try it
                      :Implicit input of string array U
 £                    :Map each X
   ¯                  :  Slice U to index
      Xk              :    Remove from X
;       B             :     The lowercase alphabet (leaving only the digits or an empty string, which is falsey)
          ªV          :    Logical OR with V (initially 0)
    V=                :    Assign the result to V for the next iteration
             +        :  Append
              Xo      :  Remove everything from X, except
;               B     :   The lowercase alphabet
  =                   :  Reassign the resulting string to U for the next iteration

Jelly, 16 bytes
⁹fØDVo©®⁸ḣ;ḟØDµ\

Try it online!
How it works
⁹fØDVo©®⁸ḣ;ḟØDµ\  Main link. Argument: A (array of strings)

              µ\  Cumulatively reduce A by the link to the left.
⁹                     Yield the right argument.
  ØD                  Yield "0123456789".
 f                    Filter; keep only digits.
    V                 Eval the result. An empty string yields 0.
     o©               Perform logical OR and copy the result to the register.
       ®              Yield the value in the register (initially 0).
        ⁸ḣ            Head; keep that many character of the left argument.
          ;           Concatenate the result and the right argument.
            ØD        Yield "0123456789".
           ḟ          Filterfalse; keep only non-digits.

Groovy, 74 bytes
{w="";d=0;it.replaceAll(/(\d*)(.+)/){d=(it[1]?:d)as int;w=w[0..<d]+it[2]}}

Try it online!
Explanation:
{                                                                        }  Closure, sole argument = it
 w="";d=0;                                                                  Initialize variables
          it.replaceAll(/(\d*)(.+)/){                                   }   Replace every line (since this matches every line) and implicitly return. Loop variable is again it
                                     d=(it[1]?:d)as int;                    If a number is matched, set d to the number as an integer, else keep the value
                                                        w=w[0..<d]+it[2]    Set w to the first d characters of w, plus the matched string

Python 2, 118 bytes
import re
n=0
l=input()
o=l.pop(0)
print o
for i in l:(N,x),=re.findall('(\d*)(.+)',i);n=int(N or n);o=o[:n]+x;print o

Try it online!

Retina 0.8.2, 69 bytes
+`((\d+).*¶)(\D)
$1$2$3
\d+
$*
+m`^((.)*(.).*¶(?<-2>.)*)(?(2)$)1
$1$3

Try it online! Link includes harder test cases. Explanation:
+`((\d+).*¶)(\D)
$1$2$3

For all lines that begin with letters, copy the number from the previous line, looping until all lines begin with a number.
\d+
$*

Convert the number to unary.
+m`^((.)*(.).*¶(?<-2>.)*)(?(2)$)1
$1$3

Use balancing groups to replace all 1s with the corresponding letter from the previous line. (This turns out to be slightly golfier than replacing all runs of 1s.)

Scala, 226 129 102 bytes
Thanks @ASCII-only for their work here (and for the Groovy answer).
s=>{var(w,d,r)=("",0,"(\\d*)(.+)".r)
s map(_ match{case r(a,b)=>{if(a>"")d=a.toInt
w=w.take(d)+b}
w})}

Try it online!

:| both links are the same

yeah, editing. I didn't know how to turn it out and was in a hurry so I did not modify what I did.

130

129

127

Stop working at new year oO

jokes aside, nicely done

Let us continue this discussion in chat.

Red, 143 bytes
func[b][a: charset[#"a"-#"z"]u: b/1 n: 0 foreach c b[parse c[copy m to a
p: copy s to end(if p<> c[n: do m]print u: rejoin[copy/part u n s])]]]

Try it online!

Perl 5 -p, 45 41 bytes
s:\d*:substr($p,0,$l=$&+$l*/^\D/):e;$p=$_

Try it online!
Explanation:
s:\d*:substr($p,0,$l=$&+$l*/^\D/):e;$p=$_ Full program, implicit input
s:   :                           :e;      Replace
  \d*                                       Any number of digits
      substr($p,0,              )           By a prefix of $p (previous result or "")
                  $l=  +                      With a length (assigned to $l) of the sum
                     $&                         of the matched digits
                          *                     and the product
                        $l                        of $l (previous length or 0)
                           /^\D/                  and whether there is no number in the beginning (1 or 0)
                                                (product is $l if no number)
                                    $p=$_ Assign output to $p
                                          Implicit output

This can be shorted by removing the parentheses in the substr clause.

Java (JDK), 150 bytes
a->{String p="",s[];for(int n=0,i=0;i<a.length;a[i]=p=p.substring(0,n=s.length<1?n:new Short(s[0]))+a[i++].replaceAll("\\d",""))s=a[i].split("\\D+");}

Try it online!

05AB1E, 20 19 17 16 bytes
õUvyþDdi£U}Xyá«=

Try it online or verify all test cases.
Explanation:
õ               # Push an empty string ""
 U              # Pop and store it in variable `X`
v               # Loop `y` over the (implicit) input-list:
 yþ             #  Push `y`, and leave only the digits (let's call it `n`)
   Ddi  }       #  If it's a non-negative integer (thus NOT an empty string ""):
      £         #   Pop and push the first `n` characters of the string
       U        #   Pop and store it in variable `X`
         X      #  Push variable `X`
          yá    #  Push `y`, and leave only the letters
            «   #  Merge them together
             =  #  Print it with trailing newline (without popping)

Jelly, 27 bytes
f€ȯ@\V,ɗḟ€ɗØDZẎḊṖḣ2/Ż;"f€Øa

Try it online!

Python 2, 101 100 99 bytes
import re
s=n='0'
for t in input():(m,w),=re.findall('(\d*)(.+)',t);n=m or n;s=s[:int(n)]+w;print s

Try it online!

C# (Visual C# Interactive Compiler), 134 bytes
a=>{int l=0,m,n;var p="";return a.Select(s=>{for(m=n=0;s[m]<58;n=n*10+s[m++]-48);return p=p.Substring(0,l=m>0?n:l)+s.Substring(m);});}

Try it online!
-9 bytes thanks to @ASCIIOnly!
Less golfed...
// a is an input list of strings
a=>{
  // l: last prefix length
  // m: current number of digits
  // n: current prefix length
  int l=0,m,n;
  // previous word
  var p="";
  // run a LINQ select against the input
  // s is the current word
  return a.Select(s=>{
    // nibble digits from start of the
    // current word to build up the
    // current prefix length
    for(m=n=0;
      s[m]<58;
      n=n*10+s[m++]-48);
    // append the prefix from the
    // previous word to the current
    // word and capture values
    // for the next iteration
    return
      p=p.Substring(0,l=m>0?n:l)+
      s.Substring(m);
  });
}

134?

That's pretty cool :) I changed l=n>0?n:l to l=m>0?n:l because it wasn't picking up the case when a line started with zero (0jkl). Thanks for the tip!

Groovy, 103 99 bytes
{w=it[0];d=0;it.collect{m=it=~/(\d+)(.+)/;i=m.find()?{d=m[0][1] as int;m[0][2]}():it;w=w[0..<d]+i}}

Try it online!

76?

74?

Common Lisp, 181 bytes
(do(w(p 0))((not(setf g(read-line t()))))(multiple-value-bind(a b)(parse-integer g :junk-allowed t)(setf p(or a p)w(concatenate'string(subseq w 0 p)(subseq g b)))(format t"~a~%"w)))

Try it online!
Ungolfed:
(do (w (p 0))   ; w previous word, p previous integer prefix (initialized to 0)
    ((not (setf g (read-line t ()))))   ; read a line into new variable g
                                        ; and if null terminate:
  (multiple-value-bind (a b)            ; let a, b the current integer prefix
      (parse-integer g :junk-allowed t) ; and the position after the prefix
    (setf p (or a p)                    ; set p to a (if nil (no numeric prefix) to 0)
          w (concatenate 'string        ; set w to the concatenation of prefix
             (subseq w 0 p)             ; characters from the previous word
             (subseq g b)))             ; and the rest of the current line
    (format t"~a~%"w)))                 ; print the current word

As usual, the long identifers of Common Lisp make it non particularly suitable for PPCG.


text:
Smallest Diversifying Exponent
A pandigital number is an integer which contains every digit from 0 to 9 at least once. 1234567890, 1902837465000000, and 9023289761326634265 are all pandigital. For the purposes of this challenge, numbers such as 123456789 are not pandigital, since they do not contain a 0, even though 123456789 = 0123456789.
A diverse pair of integers is a pair of integers \$(a, b)\$ such that \$a^b\$ is pandigital. \$b\$ is called the diversifying exponent.
Challenge: Given an integer \$a\$, find the smallest corresponding diversifying exponent \$b\$. This is a code-golf, so the shortest program in bytes wins.
(You may assume that there exists such an exponent, that is, your program will not be given invalid input, such as a power of 10.)
Your solution must be able to handle at the minimum the given test cases, but it should theoretically handle all valid inputs.
This is A090493 on OEIS.
Test cases
2 -> 68
3 -> 39
4 -> 34
5 -> 19
6 -> 20
7 -> 18
8 -> 28
9 -> 24
11 -> 23
12 -> 22
13 -> 22
14 -> 21
15 -> 12
16 -> 17
17 -> 14
18 -> 21
19 -> 17
20 -> 51
21 -> 17
22 -> 18
23 -> 14
24 -> 19
25 -> 11
26 -> 18
27 -> 13
28 -> 11
29 -> 12
30 -> 39
31 -> 11
32 -> 14
33 -> 16
34 -> 14
35 -> 19
36 -> 10
1234567890 -> 1

I want to point out a special case 1234567890 -> 1.

@Bubbler Added.

are negative exponents off limits?

@sudorm-rfslash If we want to restrict ourselves to integers, they're sure off-limits. ;-)

More seriously, by "number" you mean "integer", right?

Does something like 123456789 count as pandigital? It is equal to 0123456789, which is definitely pandigital.

@wastl no, it does not.

@EriktheOutgolfer I’ll specify.

This differs slightly from A090493 as A090493 specify 0 when no such exponent exists.

Brachylog (v2), 9 bytes
;.≜^dl10∧

Try it online!
This is a function submission. The TIO link contains a wrapper that makes a function into a full program.
Explanation
;.≜^dl10∧
 .≜        Brute-force all integers, outputting the closest to 0
;  ^         for which {the input} to the power of the number
    d        has a list of unique digits
     l10     of length 10
        ∧  (turn off an unwanted implicit constraint)

Python 2, 44 bytes
f=lambda n,k=1:11>len(set(`k`))and-~f(n,n*k)

Input has to be a long, as `k` behaves differently for longs and ints.
Try it online!

J, 25 bytes
>:@]^:(10>#@~.@":@^)^:_&1

Try it online!
Single monadic verb. The input should be an extended-precision integer (e.g. 2x).
How it works
>:@]^:(10>#@~.@":@^)^:_&1    Monadic verb. Input: base a
    ^:              ^:_      Good old do-while loop.
                       &1    Given 1 as the starting point for b,
>:@]                         increment it each step
      (            )         and continue while the condition is true:
               ":@^          Digits of a^b
            ~.@              Unique digits
          #@                 Count of unique digits
       10>                   is less than 10

(]+10>#@=@":@^)^:_*

Perl 6, 32 bytes
{first ($_** *).comb.Set>9,1..*}

Try it online!
Pretty self-explanatory.
Explanation
{                              }  # Anonymous code block
first                     ,1..*   # First positive number that
      ($_** *)    # When the input is raised to that power
              .comb.Set    # The set of digits
                       >9  # Is longer than 9

JavaScript (Node.js),  51 46  43 bytes
Takes input as a BigInt literal. Returns true instead of 1.
f=(n,k=n)=>new Set(n+'').size>9||1+f(n*k,k)

Try it online!

I keep forgetting JS has bigint's now :D

I am slightly dubious regarding returning true instead of 1. That doesn't seem to match anything described at https://codegolf.meta.stackexchange.com/questions/9263/which-number-formats-are-acceptable-in-output

@Sparr Here is the current consensus.

Thanks. I put a new Answer on my link referring to that.

05AB1E (legacy), 10 9 bytes
Saved 1 byte thanks to Mr. Xcoder
XµINmÙgTQ

Try it online!
Explanation
Xµ           # find the first positive integer N that
  INm        # when the input is raised to N
     Ù       # and duplicate digits are removed
      g      # has a length
       TQ    # equal to 10

Legacy saves 1 byte: 1µINmÙgTQ – Try it online!

@Mr.Xcoder: Oh yeah, we had the implicit output of N then. Thanks!

Ah, didn't even consider using 1µ to get the first integer $\geq0$ in the legacy version before. Although I do see I've already upvoted this answer. Anyway, mainly want to state that in the new version of 05AB1E this can be 8 bytes with the exact same approach by changing 1µIN to ∞.Δ (find the first positive integer that results in truthy for). This is one by longer than the 1µ, but the IN are done implicitly with this one, thus saving the byte. I would leave your answer as is, but just wanted to mention this for anyone else coming across this answer in the future.

Ruby, 41 bytes
->n{i=0;i+=1until(n**i).digits.uniq[9];i}

Try it online!

Haskell, 50 bytes
f a=until(\b->all(`elem`show(a^b))['0'..'9'])(+1)1

Try it online!
Same byte count:
f a=[b|b<-[1..],all(`elem`show(a^b))['0'..'9']]!!0

Charcoal, 19 bytes
ＷΦχ¬№ＩＸＩθＬυＩκ⊞υωＩＬυ

Try it online! Link is to verbose version of code. Explanation:
ＷΦχ¬№ＩＸＩθＬυＩκ⊞υω

Repeatedly push the empty string to the empty list until there are no digits that the power of the input to the length of the list does not contain.
ＩＬυ

Print the length of the list.

Why the downvote?

K (ngn/k), 76 bytes
{#{10>#?(+/|\0<|x)#x}{{+/2 99#,/|0 10\x,0}/+/99 99#,/a*\:x,0}\a::|(99#10)\x}

Try it online!
{ } function with argument x
|(99#10)\x we represent numbers as reversed lists of 99 decimal digits - do that to the argument
a:: assign to global variable a (k has no closures. we need a to be global so we can use it in subfunctions)
{ }{ }\ while the first function returns falsey, keep applying the second function (aka while loop), preserving intermediate results
a*\:x each of a's digits multiplied by each of x's digits ("outer product")
99 99#a*\:x,0 add an extra column of 0s and reshape again to 99x99, this shifts the i-th row by i items to the right, inserting 0s on the left (this works for the tests, for larger inputs 99x99 might lead to overflows)
+/ sum
{+/2 99#,/|0 10\x,0}/ propagate carry:

{ }/ keep applying until convergence
0 10\x divmod by 10 (a pair of lists)
|0 10\x moddiv by 10
2 99#,/|0 10\x,0 moddiv by 10, with the "div" part shifted 1 digit to the right
+/ sum

{10>#?(+/|\0<|x)#x} - check for (not) pandigital:

|x reverse x
0< which digits are non-zero
|\ partial maxima
+/ sum - this counts the number of leading 0s in x
10> are they fewer than 10?

# length of the sequence of powers - this is the result

Pyth, 10 8 bytes
fq;l{`^Q

Try it online here.
fq;l{`^QT   Implicit: Q=eval(input())
            Trailing T inferred
f           Return (and print) the first positive integer where the following is true:
      ^QT     Raise input to the current number-th power
     `        Convert to string
    {         Deduplicate
   l          Take the length
 q            Is the above equal to...
  ;           10

Saved 2 bytes thanks to FryAmTheEggman, previous code fq;l{j^QT;

You can use backtick to convert the number to a string instead of doing base conversion which will let you leave out the T in the power operation.

Vyxal, 11 bytes
λ?neSUL₀=;ṅ

Try it Online!
λ               ;ṅ                # First truthy integer n where
  ?ne                              # When raised to input^n
        SUL₀=                   # Length of unique digits is 10

Jelly, 12 11 bytes
1*@ṾØDfƑʋ1#

Try it online!
How it works
1*@ṾØDfƑʋ1#  Main link. Argument: n

1            Set the return value to 1.
         1#  Call the link to the left for k = 1, 2, ... and with right argument n,
             until it returns a truthy value.
        ʋ      Combine the four links to the left into a dyadic chain.
 *@              Compute n**k.
   Ṿ             Convert it to its string representation.
    ØD           Yield "0123456789".
      fƑ         Filter and return 1 is the result is equal to the left argument.

Racket, 110 96 bytes
-14 bytes thanks to UltimateHawk!
(define(f n[b 1])(if(= 10(length(remove-duplicates(string->list(~v(expt n b))))))b(f n(+ b 1))))

Try it online!

This can be shortened to 96 bytes by recursing on the function instead (define(f n[b 1])(if(= 10(length(remove-duplicates(string->list(~v(expt n b))))))b(f n(+ b 1))))

@UltimateHawk Thank you! I forgot about the default parameters...(although the helper function also used default parameter b...)

Tcl, 82 bytes
proc X d {while {[llength [lsort -u [split [expr $d**[incr i]] ""]]]-10} {}
set i}

Try it online!

You can save some more bytes with llength 82 bytes

Saved some bytes, thenks to @david

Python 3, 52 47 bytes
thanks to @BMO
f=lambda n,i=1:len({*str(n**i)})>9or 1+f(n,i+1)

Try it online!

Just a heads up but you can just remove old code and put "52 47" in the header line. The edit log will retain the old versions if anyone is curious

Java, 108 bytes
a->{int b=0;while(new java.math.BigDecimal(a).pow(++b).toString().chars().distinct().count()<10);return b;};

Try it online!
Explanation
Brute force, looping a^b until it finds a string with 10 (or more, but that's impossible as there will only be 0 through 9) unique characters.
BigDecimal is required both because Math.pow is not accurate enough (fails on case 11), and also because converting a Double to a String by default shows scientific notation, which breaks this method of finding a pandigital number.

Don't Java vars start at 0 by default?  Could save 2 bytes by eliminating the initialization.

@DarrelHoffman Instance variables do, yes. Locally-scoped variables do not.

Ah, alright.  Been some time since I worked in Java, forgot that technicality.

You can save 6 bytes by changing new java.math.BigDecimal(a).pow(++b).toString() to (new java.math.BigDecimal(a).pow(++b)+"") (and the trailing semi-colon doesn't have to be counted for lambda functions). Try it online

W d, 8 5 bytes
This answer was made to fullfill lirtosiast's bounty.
Ö↕,╣⌡

Uncompressed:
xUT=iX

Explanation
    iX % For every item in the range from 1 to infinity:
x      % Exponentiate b with a
 U     % Uniquify the value (Numbers are cast to a string)
  T=   % Is the length equal to 10?
       % (Due to the unique mechanism in W, this finds the length
       % of the string before the operation is done.)
       % If that's true, output the number and halt.

Wolfram Language (Mathematica), 48 bytes
(For[n=1,!AllTrue[DigitCount[#^n],#>0&],n++];n)&

Try it online!

Attache, 27 bytes
${Generate{#Unique[x^_]>9}}

Try it online!
Explanation
${Generate{#Unique[x^_]>9}}
${                        }    lambda, input: x
  Generate{              }     first natural number _ satisfying...
                   x^_             the input to that number
            Unique[   ]          unique digits of ^
           #                   length of ^
                       >9      is greater than 9
                               i.e.: has 10 distinct digits

Alternatives
28 bytes: ${Generate{Unique@S[x^_]@9}}
29 bytes: ${Generate{Unique[S[x^_]]@9}}
30 bytes: ${Generate{#Unique[S[x^_]]>9}}
31 bytes: Generate@${{#Unique[S[x^_]]>9}}
32 bytes: ${Generate[{#Unique[S[x^_]]>9}]}
33 bytes: ${If[#Unique[x^y]>9,y,x&$!-~y]}&0
34 bytes: ${If[#Unique[x^y]>9,y,$[x,y+1]]}&0

PowerShell, 107 bytes
param([bigint]$a)for([bigint]$b=1;-join("$([bigint]::pow($a,$b))"|% t*y|sort -u)-ne-join(0..9);$b=$b+1){}$b

Try it online!
Pretty straightforward, just a shame we need to use [bigint] everywhere. We take input $a, then setup a for loop with initializer $b=1.
Each iteration we increment $b after checking whether $a ^ $b (via pow) sent toCharArray, sorted with the -unique flag, then -joined together into a string is -notequal to the range 0..9 also -joined into a string.
That's a mouthful. For example, this would compare 7 ^ 5 = 16807 --> "01678" against "0123456789", determine they're not equal, and continue the loop.
Once we're out of the loop, we've determined which $b suits our input, and so leave that on the pipeline. Output is implicit.

Clean, 107 101 bytes
import StdEnv,Data.Integer
$a=hd[b\\b<-[1..]|length(removeDup[c\\c<-:toString(prod(repeatn b a))])>9]

Try it online!
Takes input as Integer, returns Int

GolfScript, 20 bytes
:i;0{).i\?`.|,10<}do

My first stab at it, pretty happy with my solution.
TLDR; Takes i as the base, and makes a 0. Open block. Incrememnt that 0 by 1, then put it as an exponent of i. Turn it into a string, then check how many characters there are. If 10, stop. If not, repeat block.
Successful exponent printed on program termination.
Try it online!

Perl 5 -Mbigint -n, 61 43 bytes
$t=$_**($p+=1)while grep$t!~/$_/,0..9;say$p

Try it online!

Burlesque, 18 bytes
rir0?^{NBL[10==}fi

Try it online!
ri      # Read input as int
r0      # Range [0,inf]
?^      # Raise to power
{
 NB     # Unique digits
 L[10== # Length == 10
}fi     # Find index of first which


text:
Pumping quine radix
Inspired (you don't need to know them for this challenge) by the Pumping Lemma for various languages, I propose the following challenge:

choose some basis \$B \geq 2\$ and an integer \$n \geq 0\$
write a program/function \$P\$
partition* \$P\$ into some strings \$s_i \neq \epsilon\$, st. \$P = s_n | \cdots | s_i | \cdots | s_0\$

So far easy enough, here comes the tricky part: The program \$P\$ must for any given string with \$e_i \in \mathbb{N}^+\$
$$(s_n)^{e_n} | \cdots | (s_i)^{e_i} | \cdots | (s_0)^{e_0}$$
output \$\sum_{i=0}^n e_i \cdot B^i\$ and something distinct from any positive number (eg. erroring, \$0\$, \$-1\$ etc.) for any other string.
* You must ensure that for a pumped string as described above the \$e_i\$s are unique.
Informal Explanation
Write a program and split it into a fixed number of chunks and pick a power-series (eg. \$1,2,4,8,\dotsc\$). The program needs to take a string as input and output a number in the following way:

First decide if the input string is made out of the same chunks (in order) as the program, though each chunk can be repeated any number of times. If this is not the case, return \$0\$, a negative number, error out etc.
Count the number of times each chunk is repeated and output (using the powerseries of \$2\$ as example): number_of_last_chunk * 1 + number_of_second_last_chunk * 2 + number_of_third_last_chunk * 4 + ...

Thanks Laikoni for helping me with the explanation!
Example
Suppose I have the program \$\texttt{ABCDEF}\$ and I choose \$n = 2\$ with the partitioning \$s_2 = \texttt{ABC}\$, \$s_1 = \texttt{D}\$ and \$s_0 = \texttt{EF}\$, choosing basis \$B = 2\$ we would have the following example outputs (the input is given to the original program):
$$
\begin{aligned}
  \text{Input}         &\mapsto \text{Output} \\\
  \texttt{ABCDE}       &\mapsto  0 \\\
  \texttt{ABCDEF}      &\mapsto  7 \\\
  \texttt{ABCDEFEF}    &\mapsto  8 \\\
  \texttt{ABCABCDEF}   &\mapsto 11 \\\
  \texttt{ABCDDEFEFEF} &\mapsto 11 \\\
  \texttt{ABCABCDDEF}  &\mapsto 13
\end{aligned}
$$
This submission has score \$3\$.
Walk-through
The example \$\texttt{ABCDE}\$ maps to \$0\$ because the \$\texttt{F}\$ is missing.

Now let's walk through the fifth example: The input is \$\texttt{ABCDDEFEFEF}\$ which we can write using the strings \$s_2,s_1,s_0\$ as follows:
$$
(\texttt{ABC})^1 | (\texttt{D})^2 | (\texttt{EF})^3
$$
So this gives us \$1\cdot 2^2 + 2 \cdot 2^1 + 3 \cdot 2^0 = 4+4+3 = 11\$.
Winning criterion
The score of your program/function will be \$n+1\$ where larger is better, ties will be the submission date earlier is better.
In case you're able to generalize your submission to an arbitrarily large \$n\$, you may explain how and score it as \$\infty\$.

Notations: \$\epsilon\$ denotes the empty string, \$x | y\$ the string concatenation of \$x\$ and \$y\$ & \$x^n\$ is the string \$x\$ repeated \$n\$ times.

It took me quite I while to get what you are asking even though I'm generally familiar with formal notation. I think it would be nice to give an informal high-level explanation of what the program needs to do at the beginning and additionally at least one worked out example which includes all steps to get to the output number.

@Laikoni: Done so, is it of any help? It's not easy to determine how high-level such an informal explanation should be, that's why I added an example. I'll add an explanation of one input/output-tuple on why it results in that number.

@Arnauld: Yes it is, I'll emphasize in the specification.

@BMO It's still pretty mathy, though I think the explanation of the example is quite helpful.

For the ABCDEF example, why is it allowed when the eᵢs are not unique?

@LegionMammal978: Maybe I don't understand the question, but, the $\texttt{E}$ is in $s_0$ so it's always unique.

Oh, so you mean that there exists only one possible assignment of the $e_i$s for any possible string? I misinterpreted it as $e_i\neq e_j$ for $i\neq j$.

@LegionMammal978: Yes, that was what I meant to say. For example you can't have something where $s_3 = \texttt{x} = s_4$.

Mathematica, \$B=2\$, score \$\infty\$
With[{s =
    StringJoin[StringRepeat["a", #] <> "b" & /@ Reverse[Range[#]]]},
  ToString[(s;
     StringReplace[#,
       StartOfString ~~ a : "(\"" .. ~~ b : ("a" .. ~~ "b") .. ~~
          c : ("\"; " <>
              StringTake[
               ToString[Extract[#0, {1, 2}, Hold],
                InputForm], {6, -2}] <> ") & ") .. ~~ EndOfString /;
         AllTrue[Differences[
            d = Length /@ Split[Characters[b]][[;; ;; 2]]], -2 < # <
             1 &] && Length[d] > 1 :>
        2^Max[d] StringLength[a] + Total[2^# #2 & @@@ Tally[d]] +
         StringLength[c]/389][[1]]) &, InputForm]] &

Pure function. Takes \$k=n-1\$ as input and returns the string representing \$P_k\$ as output (for \$k\geq2\$). For the purposes of illustration, take this formatted version of \$P_3\$:
("aaabaabab";
  StringReplace[#1,
    StartOfString ~~ a : ("(\"" ..) ~~ b : (("a" .. ~~ "b") ..) ~~
       c : ("\"; " <>
           StringTake[
            ToString[Extract[#0, {1, 2}, Hold], InputForm], {6, -2}] <>
            ") & " ..) ~~ EndOfString /;
      AllTrue[Differences[
         d = Length /@
           Split[Characters[b]][[1 ;; All ;; 2]]], -2 < #1 < 1 &] &&
       Length[d] > 1 :>
     2^Max[d] StringLength[a] +
      Total[Apply[2^#1 #2 &, Tally[d], {1}]] + StringLength[c]/389][[
   1]]) &

This function takes a string as input and either returns a positive integer or an invalid expression ("..."[[1]]) as output. Here, \$s_4=\texttt{("}\$, \$s_3=\texttt{aaab}\$, \$s_2=\texttt{aab}\$, \$s_1=\texttt{ab}\$, and \$s_0\$ encompasses the remainder of the function.

Sadly I don't know Mathematica, can you provide a way of testing this?

@BMO Here's the generation program

@BMO Here's P₃