[Question]
  [
Given a string, like `potatocarrot`, break it into the smallest number of substrings possible. These substrings can consist either be a single character, or one of a list of words passed as input. For example, if `[carrot, potato]` was the input, the optimal way to break the string up would be `[potato][carrot]`. If the list was `[potat, tatocarr, rot]`, the optimal way would be `[p][o][tatocarr][o][t]`.
You can take the string in any reasonable format, and the list of substrings in any reasonable format. As for output, any of the following would be examples of allowed output formats:
* Inserting characters into the string to delimit substrings (e.g., `"potato,carrot"` or `"[potato][carrot]"`)
* Delimiting words, but not individual characters (e.g., `"po[tatocarr]ot"`, but not `"po,tatocarr,ot"`)
* Returning an array of substrings (e.g., `["potato", "carrot"]`)
You can assume the input strings consist of lowercase letters, and that there won't be duplicates or strings with a length of 0 or 1 in the list of substrings.
This is [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'"), so shortest answer (in bytes) per language wins.
**Test cases:**
```
STRING          WORDS                   OPTIMAL SOLUTION
potatocarrot    potato, carrot          [potato][carrot]
potatocarrot    pot, tatocarr, rot      [p][o][tatocarr][o][t]
potatocarrot    atocarr, ot             [p][ot][atocarr][ot]
potatocarrot    tatocarr, potato, carr  [potato][carr][o][t]
purpur                                  [p][u][r][p][u][r]
purpur          carrot                  [p][u][r][p][u][r]
purpur          pur                     [pur][pur]
purpur          pu, rp                  [pu][r][pu][r] OR [pu][rp][u][r]
purpur          pu, rp, urp             [pu][r][pu][r] OR [pu][rp][u][r] OR [p][urp][u][r]
todoornottodo   to, do, or, not         [to][do][or][not][to][do]
todoornottodo   tod, nott, door         [t][o][door][nott][o][d][o]
```
  
[Answer]
# [Python](https://www.python.org), 82 bytes
```
f=lambda s,d:s and min([[w,*f(s[len(w):],d)]for w in[*d,*s]if w+"~">s>=w],key=len)
```
[Attempt This Online!](https://ato.pxeger.com/run?1=lZLBagMhEIbpNU8hnnRj7iWweYhexYOtkSxNnEVnkVz6Ir3k0r5T8gR5jLq6gXRZyEZEZsbvH-ZHv3_bI-7AnU4_HdrV6_nN1nt9eDeaBGHWgWhnyKFxTMooKsuC3G8di3ythOHKgieRNE5WRlRBNZbEJf2im7CpoxKf22OdaF46X16urW8cMstoC6gRPrT3gFSQtORQSxkd6orzxSNFj98u-niW7o6fg9_3_z_kSNv5tIuqLDltZYLrCzOgbLF9gkxnNxYgGADvAPsg65LH7CmnFLJTNx57WmYGFoseso3y4Lcv9Qc)
Depth-first search over solution space.
-6 bytes thanks to loopy walt
[Answer]
# [APL(Dyalog Unicode)](https://dyalog.com), 42 bytes [SBCS](https://github.com/abrudz/SBCS)
Assumes index origin 0. Takes the string as right argument ⍵ and the word list as left argument ⍺.
```
{e⊃⍨⊃⍋≢¨e←d/⍨(1∧.=≢¨∨∊∘⍺)¨d←⊂∘⍵¨~,⍳1+×⍳≢⍵}
```
[Try it on APLgolf!](https://razetime.github.io/APLgolf/?h=AwA&c=q0591NX8qHcFmOx@1Lno0IrUR20TUvSBYhqGjzqW69mCBR91AFHXo44Zj3p3aR5akQJU86irCczfemhFnc6j3s2G2oenAymgcqBYLQA&f=e9Q31dP/UdsEAy71gvySxJL85MSiovwSdQWFtEe9KxQedbVBxdUV1KFSeFUClcHkgEz8yhHq8CpDMg/FKSAtpUVABFQMAXAtCnjkEL7AoQAkiE8W5K8CIlQoqJeC1ZXkp@TnF@Xll4AY6kj@AnkEJKKeD/JbHthRuBWnQNSUgDUBdQAA&i=AwA&r=tryapl&l=apl-dyalog&m=dfn&n=f)
`⍳≢⍵` Indices of the string: `0 1 ... (length-1)`  
`1+×` Sign of that incremented: `1 2 2 ... 2 2`  
`,⍳` Indices of an array of that shape, flattened. All these indices have a 0 in the first position and some combination of 1's and 0's after that.  
`~` Boolean inverse.  
`d←⊂∘⍵¨` Partition the string according to each of these sequences. This yields a list of all partitions. (Call this `d`)  
`≢¨∨∊∘⍺ ¨` For each substring in each partition, take the gcd of its length and the boolean indicating whether it is in the word list. This is only 1 if at least one of the values is 1.  
`1∧.=` For each partition, are all values 1?  
`e←d/⍨` Only keep partitions where this is the case and call these `e`.  
`e⊃⍨⊃⍋≢¨` Pick the partition with minimal length.
[Answer]
# [Ruby](https://www.ruby-lang.org/), ~~60~~ 53 bytes
```
->s,l,r=""{s=~/^#{r+="(.|"+l*?|+?)}$/?$~[1..-1]:redo}
```
[Try it online!](https://tio.run/##lZNBb4IwFMfvfIqGmUVnxeHBg5ERDx48OJNtyQ5NZ5xoQsJoU8phEf3q7LUFIcBm1kBp/6@//2v6ikg/v/Ojl4@eEhxh4dn2KfEu44@7kxh6dt/J7GH04GdDf3Dujf3ehbiOM3LpTBwCds6jMD4k3nz@vtmuXt@26@XqebldL9YLizO5k2y/E4JJBM3MMaoU04gJUGICtAvEqJQwusKEUwJYGTGTNn4F60lLXFJS4R1wlba@/camr5lTAQ@62VTmlBLgykELbR7SP9DftkAgQHXXgcCx8k7E5NIftHkplD9yKyOM0obbLSOjwKSylixgTMRMqoGqBJx9AC@DWsS1oyGqCgG8DPxjVdBC6HAINCqVERM1B11ApWmHYqp6q32t9YV3TP@146dsn1kIJd6ePGJ3Sp1EipCDEoHiTvFkAhKPQtn38UAB/fuZXjKANRwdCfx01DrnPw "Ruby – Try It Online")
[Answer]
# [Retina](https://github.com/m-ender/retina/wiki/The-Language), ~~81~~ 65 bytes
```
/,;/^+%L$w`\b(\w(\w+)?)(\w*;(?(2).*\b\1\b))
$`$1,$3$'
0G`,;
,;.*
```
[Try it online!](https://tio.run/##ZY09DoMwDIV3nyOoCVhQ2tEDI0uPEFWB0qEDuIpccfw0COgPSLb8vvdk29/lMTRlSHTtQoFUXLPkokZnW23HWJmpTBwp6UqfTJ7a1pa2NQaUUyWqszrAsXZIgJSnEMKTpRG@Nd6z0Aw4E2wjXBF36Rps/c/Gz2XoXz4WrXP5tlDsr0Tf/wFGDcIdsx9YJkHxaMfIHqOxi7rJFZzMNw "Retina – Try It Online") Link includes test cases. Explanation:
```
/,;/^+
```
Repeat until a complete substring list has been found...
```
%L$w`\b(\w(\w+)?)(\w*;(?(2).*\b\1\b))
```
... find all of the leading substrings of the rest of the input that are either a single character or are present in the list of words, ...
```
$`$1,$3$'
```
... and create new substring lists containing each of those substrings.
```
0G`,;
,;.*
```
Output just the first complete list of substrings.
[Answer]
# [Python](https://www.python.org), ~~353~~ ~~348~~ ~~342~~ 309 bytes
```
import re,itertools as I
def f(s,a):
 u=[]
 for e in I.permutations(a):
  *r,=s
  for x in e:
   if x in s:
    p=0
    for _ in"_"*s.count(x):
     b=s.index(x,p);p=b+len(x);j=''.join(r[b:p])
     if j.isalpha():r[b]='<'+r[b];r[p-1]+='>'
  u+=''.join(r),
 return min(u,key=lambda x:len(re.sub('<.+?>','',x)))
```
[Attempt This Online!](https://ato.pxeger.com/run?1=fVPNbpwwEFZz5ClGXIyDg9pDpWoX03OeAaHIG4zClrUt_0jkDfIOveTSPlSfpmPDZldKiIQ9ns_fzOcx499_zbN_0ur19U_ww92Pf1--jyejrQcr2eil9VpPDoSD-6yXAwyFY4LuMgi87TIYtAUJo4L7ykh7Cl74UStXJArcWsYd2siaI0tGFMZh8VzywPCvyUbWA-L5Q37rqkcdlC9munDgwF01ql7OxcwM3Rt-KCepcH9_5IRURz2qwraHnenoEoAix2p0YjJPoqA73Os4qUkZF3vbmrtvXclJQ5AdyksKyjKs3Aer4IR-YL_kM5_E6dALmHdR0srKhUNB6qr82RBGCJsppevt3bx46bwDDi0mbnOj8Ub0o7BW-5zB6jO4IPUCNfUCNXnHtiIZnCEGb2h9xhpENmLfotagGnXqc1j9iehF7_rg7459LR0sfpGRlBbv3d5VWRuM1dRomzR9RMFrMCursWnaYjEIW1Sve62t0j4uUtFYZo9DY9lq_Umx0h6HxtOoeHsr8EmSPkX7mEvbBarjMiXwTeR1WZdlse0dA8GWh5T6Jza9xSaKrw0ECNWDqJyZRl8QBoTi2aBNnW7siM8EuRwkFooynKOYXBvy_Kz_Aw)
*-5 bytes thanks to ophact*
*-33 bytes thanks to pxeger*
[Answer]
# JavaScript (ES6), ~~104 102 97~~ 96 bytes
Expects `(word_list)(string)`. Returns a list of substrings.
```
a=>g=(s,O=o='',...b)=>[s[O.length],...a].every(e=>e&&g(s,O+e,...b,e))|O!=s||b[o&&o.length]?o:o=b
```
[Try it online!](https://tio.run/##rZLNToQwEMfvPgX2wNKI7N2k@AgkXpse@OiihjCklE1MeHfs5y4EWFdjA0M7w/z@03Y@83Pel@Kjk88tVHw6kSknaU2iPs4IkMMhTpKkwCSlPc2Shre1fGfal7OEn7n4ijhJeRjWOuGJm79jjvGYPZJ@HAsKYQg@7xVegBRTCW0PDU8aqKNTRFEHMpeA4gCVuRAgUXAZDEcu7EMYB8djQK2TUetmDxtMDfSZeu7J@0xGFdEH7GJNnhEXpf5EloxeyRvceanLE0F3ncJOvcGNYbiDUI/fx7XegVGF9JNVtaub@hfqLOd@6qCJwybNXHuHfkWzFZpPkL05z42KnYayw1LqrxrWoxb7qrY3KmPBdEzrL0OrSqgAhHLpCXKquk8q9YKSbnU3OscGvHJEaVXgsoF9uOk@HTBwt9R2@gY "JavaScript (Node.js) – Try It Online")
[Answer]
# Python3, 145 bytes:
```
lambda s,w:min(f(s,w),key=len)
f=lambda s,w,c=[]:[c]if''==s else[x for y in w for x in f(s[len(y):],w,c+[y])if s[:len(y)]==y]+f(s[1:],w,c+[s[0]])
```
[Try it online!](https://tio.run/##dYzBDsIgDEDv@4reoBkxGi9mCVd/AnuYc0TiBgRINr5@Dt3iydvr62t9Tk9nzxcflqu8LUM73h8tRDE1o7Fc85VQvPosh95ipeUvEJ1U1KiOjGZMygj9EHs1g3YBMhgL0wfngusftT7gGRsql7XKhEZDVM1Xk5SZ6pKd9iKqIxEuPhib@JUz71KbXNeG4BIToDaxItskIVb/8jIL2JWAYg/RDybxskXE5Q0)
[Answer]
# [Charcoal](https://github.com/somebody1234/Charcoal), ~~73~~ 67 bytes
```
≔⟦⟦Ｓ⟧⟧θ≔⟦⟧ηＷＳ⊞ηιＦθ«≔⊟ιζ¿ζＦ⁺η⟦§ζ⁰⟧Ｆ⁼κ✂ζ⁰Ｌκ⊞θ⁺ι⟦κ✂ζＬκ⟧⊞υι»⊟Φυ⁼Ｌι⌊ＥυＬλ
```
[Try it online!](https://tio.run/##VU87a8MwEJ6jX3HjHajQPVOGFgINGDIaD8ZRrCOKZOvRFpf@dleyHdpqEHf6nup06zvXmnk@hMC9xbo@2iHFc/Rse6SmkTDSXjzQvOq8fmg2CvAflaBKQaOWwJlxdR5wJPgSu01buQGZJEwZ3fEVcCJYWJVJocjqQzzai/rEScIzNbTBL2NqTcCbhLPhTi2ohDdl@6jxRvQIHiUsTpyd/pJ/mdkyRysT1KpIa9VvUeUfxKXgK5uofEG22E1dip/Y8j3d8dQOhbAhhtazn@foLs5562IZRL5EmUV5FPPTu/kB "Charcoal – Try It Online") Link is to verbose version of code. Takes input as a newline-terminated list of words and outputs each substring on its own line (default output for an array of substrings). Explanation:
```
≔⟦⟦Ｓ⟧⟧θ
```
Start a breadth-first search with an empty substring list that still has the initial input to processes.
```
≔⟦⟧ηＷＳ⊞ηι
```
Input the list of words.
```
Ｆθ«
```
Loop over the list of substring lists.
```
≔⊟ιζ
```
Remove the remainder of the input from this substring list.
```
¿ζ
```
If there is still any input left:
```
Ｆ⁺η⟦§ζ⁰⟧
```
For each of the input words, plus the first letter of the remainder of the input, ...
```
Ｆ⁼κ✂ζ⁰Ｌκ
```
... if the remainder of the input begins with this string, ...
```
⊞θ⁺ι⟦κ✂ζＬκ⟧
```
... then push a new substring list obtained by splitting the input after that string.
```
⊞υι
```
Otherwise, save this substring list.
```
»⊟Φυ⁼Ｌι⌊ＥυＬλ
```
Output any one shortest substring list.
]