# Find all English words that can be comprised of Element Abbreviations found in the Periodic Table

This question is majorely influenced by a meme...

Now, I thought... it would be interesting to see what kind of "passwords" could one derive using Periodic Table :D Hence, I began to work towards a solution. My goal is to derive an output something like seen in the meme..

SeCoNd, BReAcK, FAsTe etc.

Here is where I am just now with my solution...

DictionaryLookup[# ~~ ___] & /@ (ElementData[#, "Abbreviation"] & /@ Range @ 118)


I am not too familiar with this side of Mathematica. So, I would welcome your ideas while I work on this little project :D

• Store all the elements in lower case as elements. DictionaryLookup[RegularExpression@StringRiffle[elements, {"(", "|", ")"}]..]. Output length: 13573 Mar 29 at 16:05
• I think you could use a prefix trie approach to enumerate the options, too if you want an exhaustive search Mar 30 at 22:21

Here are some ideas that may be useful. We need a list of atomic symbols and get the names of elements too.

{atomicSymbols, names} = Transpose[
EntityValue[EntityClass["Element", All], {
EntityProperty["Element", "AtomicSymbol"],
EntityProperty["Element", "Name"]}]];


Here are three methods we can use to match atomic symbols to and the letters of a word. We need to know if a string can be matched to a sequence of symbols, which we do with a modified version of Beny Izd's comment.

Method 1: Test if a string matches a sequence of atomic symbols. stringSymbolMatchQ returns true if there's a sequence of atomic symbols that match the string, otherwise false.

Next, we need a way to find all the symbols that we can make from the letters of a word.

Method 2: Use symbolMatch to find every atomic symbol that matches the letters of a word, assuming stringSymbolMatchQ[word] is true.

When we've found a sequence of atomic symbols, we need to know their atomic numbers.

Method 3: Find the atomic numbers that match a sequence of atomic symbols - returns Missing[KeyAbsent,...] for invalid symbols.

(* Method 1 *)
stringSymbolMatchQ[s_String] := StringMatchQ[s,
RegularExpression@StringRiffle[atomicSymbols, {"(", "|", ")"}]..,
IgnoreCase -> True]

(* Method 2 *)
symbolMatch[word_String] := Module[{w = ToLowerCase[word], ch},
ch = Characters[w];
Sort@Intersection[ToLowerCase@atomicSymbols, Join[
ch,
StringJoin/@Permutations[ch, {2}]
]]]

(* Method 3 *)
symbolIndex = PositionIndex[ToLowerCase@atomicSymbols];

seq={"cd", "db", "n", "os", "q"};
Flatten[symbolIndex /@ seq]
(* {48, 105, 7, 76, Missing[KeyAbsent,q]} *)


Find a list of dictionary words that we can make from atomic symbols, again using a modified version of Beny Izd's comment.

words = DictionaryLookup[
RegularExpression@StringRiffle[atomicSymbols, {"(", "|", ")"}]..,
IgnoreCase -> True] ~ DeleteDuplicatesBy ~ ToLowerCase;
(* 15,314 words *)


## Trivia

Two letters, j and q, are not used by any atomic symbol. The same two letters are the only ones that are missing from every element name.

Complement[CharacterRange["a", "z"],
Flatten@Characters@ToLowerCase@atomicSymbols]

Complement[CharacterRange["a", "z"], Flatten@Characters@names]


## Examples

lasagna

We can find a sequence of atomic symbols that spell out a word by manually matching the letters of the word to the atomic symbols. For example, let's try lasagna. We need to find the symbols that can be made from the letters of lasagna; use symbolMatch find the symbols.

word = "lasagna";
symbolMatch[word]
(* {ag, al, as, ga, la, n, na, s, sg, sn} *)


Now we can match the letters to symbols. The first two letters match the symbol la. The next two letters (sa), don't match a symbol, but s is a match. The next match is ag, and the final two letters match na. The sequence of atomic symbols the spell lasagna is la, s, ag, and na. We find the atomic numbers that match the symbols from symbolIndex, and join the numbers into a string.

Note: Atomic numbers greater than 99 are truncated to their last two digits. Alternatively, don't pad with leading zeros, and join the one-, two- or three-digit numbers into a string.

seq = {"la","s","ag","na"};
pos = Flatten[symbolIndex/@seq];
StringJoin[IntegerString[pos, 10, 2]]
(* 57164711 *)


We want to check that the sequence is correct. Get the atomic symbols for the sequence, combine them into a string, and compare the result to the original word.

StringRiffle[atomicSymbols[[pos]]]
ToLowerCase@StringJoin[atomicSymbols[[pos]]] == ToLowerCase@word
(* La S Ag Na *)
(* True *)


Here are the names of the elements that match the sequence of atomic symbols.

names[[pos]]
(* {lanthanum, sulfur, silver, sodium} *)


secondbreackfaste

As a second example, let's try secondbreackfaste. First, check that secondbreackfaste can be matched to a sequence of atomic symbols. Then we can match the symbols and check the sequence.

word="secondbreackfaste";
stringSymbolMatchQ[word]
(* True *)
symbolMatch[word]
(* {"ac","ar","as","at","b","ba","be","bk","br","c","ca","cd","ce","cf","cn","co","cr","cs","db","ds","er","es","f","fe","fr","k","kr","n","na","nb","nd","ne","no","o","os","ra","rb","re","rf","rn","s","sb","sc","se","sn","sr","ta","tb","tc","te","ts"} *)

seq = {"se", "co", "nd", "b", "re", "ac", "k", "f", "as", "te"};
pos = Flatten[symbolIndex /@ seq];
StringJoin[IntegerString[pos, 10, 2]]
StringRiffle@atomicSymbols[[pos]]
ToLowerCase@StringJoin[atomicSymbols[[pos]]] == ToLowerCase@word
(* 34276005758919093352 *)
(* Se Co Nd B Re Ac K F As Te *)
(* True *)

• All I can say is... Amazing effort! Apr 4 at 14:52
• Thank you. The next step would be to automatically convert the word to a list of atomic symbols, which might be done with a stack that would make it possible to back out of dead-end (invalid) solutions. Apr 9 at 2:51

I'm ignoring the conversion to a number because that's trivial once you have a valid phrase in place.

Here's a prefix trie approach to getting words. It won't be fast since DictionaryLookup is slow, but I think this is about as good as you can do if you're trying to get an exhaustive list.

First we get all the classes of prefixes, and define some stuff to build a trie

classes = ToLowerCase@EntityValue[EntityClass["Element", All], "Abbreviation"];
validPatternQ[p_] :=
Length[DictionaryLookup[p, 1, IgnoreCase -> True]] > 0;
validStartQ[p_] :=
validPatternQ[p <> "*"];
validStartQ[p_, q_] :=
validStartQ[p <> q];

$$defaultTrieDepth = 1; ClearAll[fillTrie]; fillTrie[pref_, classes_, test_, depth : _Integer :$$defaultTrieDepth] :=

Block[{curTrie = <||>, t},
Do[
If[test[pref, k],
If[depth > 0,
t = fillTrie[pref <> k, classes, test, depth - 1];
curTrie[k] = If[Length[t] == 0, pref <> k, t],
curTrie[k] = pref <> k
]
],
{k, classes}
];
curTrie
]


This bottoms out at some depth (I set that to 1 out of laziness) and just checks if attaching the next prefix on the previous one has matches any valid word patterns. I'm guessing internally DictionaryLookup is using a similar kind of trie.

Now we can start from the empty string and start building (I elided some of the output)

wat = fillTrie["", classes, validStartQ, 1]

<|"h" -> <|"b" -> "hb", "o" -> "ho", "al" -> "hal", "ar" -> "har",
"as" -> "has", "y" -> "hy", "mo" -> "hmo", "ag" -> "hag", "in" -> "hin",
"i" -> "hi", "eu" -> "heu", "er" -> "her", "tm" -> "htm", "yb" -> "hyb",
"os" -> "hos", "ir" -> "hir", "au" -> "hau", "at" -> "hat", "ac" -> "hac",
"u" -> "hu", "am" -> "ham", "es" -> "hes", "og" -> "hog"|>,
"he" -> <|"li" -> "heli", "be" -> "hebe", "b" -> "heb", "c" -> "hec",
"n" -> "hen", "f" -> "hef", "al" -> "heal", "si" -> "hesi", "p" -> "hep",
"s" -> "hes", "ar" -> "hear", "ca" -> "heca", "cu" -> "hecu", "ge" -> "hege",
"br" -> "hebr", "rb" -> "herb", "y" -> "hey", "mo" -> "hemo",
"in" -> "hein", "te" -> "hete", "i" -> "hei", "xe" -> "hexe", "nd" -> "hend",
"w" -> "hew", "re" -> "here", "ir" -> "heir", "pt" -> "hept",
"pb" -> "hepb", "at" -> "heat", "rn" -> "hern", "ra" -> "hera",
"pa" -> "hepa", "u" -> "heu", "np" -> "henp", "lv" -> "helv"|>,
"li" -> <|"li" -> "lili", "be" -> "libe", "b" -> "lib", "c" -> "lic",
"n" -> "lin", "o" -> "lio", "f" -> "lif", "ne" -> "line", "na" -> "lina",
"p" -> "lip", "s" -> "lis", "ar" -> "liar", "k" -> "lik", "ti" -> "liti",
"v" -> "liv", "mn" -> "limn", "fe" -> "life", "co" -> "lico", "ni" -> "lini",
"ga" -> "liga", "br" -> "libr", "mo" -> "limo", "tc" -> "litc",
"sb" -> "lisb", "te" -> "lite", "ba" -> "liba", "la" -> "lila",
"ce" -> "lice", "pr" -> "lipr", "nd" -> "lind", "eu" -> "lieu",
"tm" -> "litm", "ta" -> "lita", "re" -> "lire", "pt" -> "lipt",
"bi" -> "libi", "po" -> "lipo", "ra" -> "lira", "th" -> "lith",
"pa" -> "lipa", "es" -> "lies", "no" -> "lino", "ds" -> "lids"|>,
"be" -> <|"h" -> "beh", "he" -> "behe", "li" -> "beli", "b" -> "beb",
"c" -> "bec", "n" -> "ben", "o" -> "beo", "f" -> "bef", "ne" -> "bene",
"na" -> "bena", "si" -> "besi", "s" -> "bes", "cl" -> "becl", "ar" -> "bear",
"k" -> "bek", "ca" -> "beca", "ti" -> "beti", "v" -> "bev", "fe" -> "befe",
"co" -> "beco", "ni" -> "beni", "ga" -> "bega", "ge" -> "bege",
"as" -> "beas", "se" -> "bese", "rb" -> "berb", "y" -> "bey", "mo" -> "bemo",
"tc" -> "betc", "ag" -> "beag", "in" -> "bein", "te" -> "bete",
"i" -> "bei", "la" -> "bela", "nd" -> "bend", "sm" -> "besm", "ho" -> "beho",
"er" -> "beer", "lu" -> "belu", "ta" -> "beta", "w" -> "bew",
"re" -> "bere", "ir" -> "beir", "au" -> "beau", "at" -> "beat",
"rn" -> "bern", "fr" -> "befr", "ra" -> "bera", "ac" -> "beac",
"th" -> "beth", "u" -> "beu", "am" -> "beam", "es" -> "bees", "db" -> "bedb",
"ds" -> "beds", "rg" -> "berg", "ts" -> "bets"|>,
"b" -> <|"h" -> "bh", "li" -> "bli", "b" -> "bb", "c" -> "bc", "o" -> "bo",
"al" -> "bal", "ar" -> "bar", "as" -> "bas", "y" -> "by", "ru" -> "bru",
"ag" -> "bag", "in" -> "bin", "i" -> "bi", "la" -> "bla", "eu" -> "beu",
"ho" -> "bho", "er" -> "ber", "yb" -> "byb", "lu" -> "blu", "re" -> "bre",
"os" -> "bos", "ir" -> "bir", "au" -> "bau", "at" -> "bat", "rn" -> "brn",
"ra" -> "bra", "ac" -> "bac", "u" -> "bu", "am" -> "bam", "es" -> "bes",
"og" -> "bog"|>,
"c" -> <|"h" -> "ch", "he" -> "che", "li" -> "cli", "b" -> "cb", "n" -> "cn",
"o" -> "co", "f" -> "cf", "al" -> "cal", "p" -> "cp", "s" -> "cs",
"ar" -> "car", "ni" -> "cni", "as" -> "cas", "y" -> "cy", "ru" -> "cru",
"ag" -> "cag", "in" -> "cin", "te" -> "cte", "i" -> "ci", "la" -> "cla",
"ho" -> "cho", "er" -> "cer", "yb" -> "cyb", "lu" -> "clu", "w" -> "cw",
"re" -> "cre", "os" -> "cos", "ir" -> "cir", "au" -> "cau", "at" -> "cat",
"ra" -> "cra", "ac" -> "cac", "u" -> "cu", "pu" -> "cpu", "am" -> "cam",
"es" -> "ces", "og" -> "cog"|>,
"n" -> <|"h" -> "nh", "b" -> "nb", "c" -> "nc", "o" -> "no", "f" -> "nf",
"ar" -> "nar", "k" -> "nk", "ca" -> "nca", "co" -> "nco", "ga" -> "nga",
"as" -> "nas", "kr" -> "nkr", "y" -> "ny", "ag" -> "nag", "in" -> "nin",
"i" -> "ni", "ba" -> "nba", "eu" -> "neu", "er" -> "ner", "os" -> "nos",
"ir" -> "nir", "au" -> "nau", "at" -> "nat", "ac" -> "nac", "th" -> "nth",
"u" -> "nu", "am" -> "nam", "es" -> "nes", "fl" -> "nfl", "og" -> "nog"|>,
"o" -> <|"h" -> "oh", "li" -> "oli", "be" -> "obe", "b" -> "ob", "c" -> "oc",
"n" -> "on", "o" -> "oo", "f" -> "of", "ne" -> "one", "na" -> "ona",
"si" -> "osi", "p" -> "op", "s" -> "os", "ar" -> "oar", "k" -> "ok",
"ca" -> "oca", "sc" -> "osc", "ti" -> "oti", "v" -> "ov", "mn" -> "omn",
"fe" -> "ofe", "ni" -> "oni", "cu" -> "ocu", "as" -> "oas", "kr" -> "okr",
"rb" -> "orb", "y" -> "oy", "in" -> "oin", "sb" -> "osb", "i" -> "oi",
"xe" -> "oxe", "ba" -> "oba", "la" -> "ola", "ce" -> "oce", "pr" -> "opr",
"sm" -> "osm", "eu" -> "oeu", "gd" -> "ogd", "dy" -> "ody", "ho" -> "oho",
"er" -> "oer", "w" -> "ow", "re" -> "ore", "pt" -> "opt", "bi" -> "obi",
"po" -> "opo", "at" -> "oat", "rn" -> "orn", "ra" -> "ora", "th" -> "oth",
"pa" -> "opa", "u" -> "ou", "pu" -> "opu", "es" -> "oes", "md" -> "omd",
"no" -> "ono", "sg" -> "osg", "hs" -> "ohs", "rg" -> "org"|>,
"f" -> <|"li" -> "fli", "b" -> "fb", "n" -> "fn", "o" -> "fo", "al" -> "fal",
"ar" -> "far", "as" -> "fas", "ru" -> "fru", "ag" -> "fag", "in" -> "fin",
"i" -> "fi", "la" -> "fla", "eu" -> "feu", "er" -> "fer", "lu" -> "flu",
"re" -> "fre", "os" -> "fos", "ir" -> "fir", "au" -> "fau", "bi" -> "fbi",
"at" -> "fat", "ra" -> "fra", "ac" -> "fac", "u" -> "fu", "am" -> "fam",
"es" -> "fes", "og" -> "fog"|>,
"ne" -> <|"h" -> "neh", "he" -> "nehe", "b" -> "neb", "c" -> "nec",
"o" -> "neo", "f" -> "nef", "al" -> "neal", "p" -> "nep", "s" -> "nes",
"ar" -> "near", "v" -> "nev", "cr" -> "necr", "fe" -> "nefe", "ga" -> "nega",
"ge" -> "nege", "br" -> "nebr", "ru" -> "neru", "i" -> "nei",
"ce" -> "nece", "tb" -> "netb", "w" -> "new", "re" -> "nere", "pt" -> "nept",
"po" -> "nepo", "at" -> "neat", "th" -> "neth", "pa" -> "nepa",
"u" -> "neu", "rf" -> "nerf", "ts" -> "nets", "og" -> "neog"|>,
"na" -> <|"h" -> "nah", "b" -> "nab", "c" -> "nac", "n" -> "nan", "o" -> "nao",
"f" -> "naf", "ne" -> "nane", "na" -> "nana", "p" -> "nap", "s" -> "nas",
"k" -> "nak", "sc" -> "nasc", "ti" -> "nati", "v" -> "nav", "cr" -> "nacr",
"ga" -> "naga", "y" -> "nay", "tc" -> "natc", "te" -> "nate", "i" -> "nai",
"ce" -> "nace", "ta" -> "nata", "ir" -> "nair", "bi" -> "nabi",
"po" -> "napo", "rn" -> "narn", "ra" -> "nara", "ac" -> "naac",
"th" -> "nath", "pa" -> "napa", "u" -> "nau", "no" -> "nano"|>, "mg" -> <||>,
"al" -> <|"h" -> "alh", "he" -> "alhe", "li" -> "alli", "be" -> "albe",
"b" -> "alb", "c" -> "alc", "n" -> "aln", "o" -> "alo", "f" -> "alf",
"p" -> "alp", "s" -> "als", "ar" -> "alar", "k" -> "alk", "ca" -> "alca",
"ti" -> "alti", "v" -> "alv", "co" -> "alco", "ni" -> "alni", "cu" -> "alcu",
"ga" -> "alga", "ge" -> "alge", "as" -> "alas", "y" -> "aly",
"mo" -> "almo", "in" -> "alin", "te" -> "alte", "i" -> "ali", "ba" -> "alba",
"la" -> "alla", "ce" -> "alce", "eu" -> "aleu", "er" -> "aler",
"tm" -> "altm", "lu" -> "allu", "ta" -> "alta", "w" -> "alw", "re" -> "alre",
"bi" -> "albi", "po" -> "alpo", "fr" -> "alfr", "ac" -> "alac",
"th" -> "alth", "pa" -> "alpa", "u" -> "alu", "am" -> "alam", "cm" -> "alcm",
"es" -> "ales", "ts" -> "alts"|>,
"si" -> <|"h" -> "sih", "li" -> "sili", "be" -> "sibe", "b" -> "sib",
"c" -> "sic", "n" -> "sin", "o" -> "sio", "f" -> "sif", "ne" -> "sine",
"na" -> "sina", "p" -> "sip", "s" -> "sis", "k" -> "sik", "ti" -> "siti",
"v" -> "siv", "ni" -> "sini", "se" -> "sise", "mo" -> "simo", "tc" -> "sitc",
"te" -> "site", "xe" -> "sixe", "cs" -> "sics", "la" -> "sila",
"nd" -> "sind", "er" -> "sier", "lu" -> "silu", "ta" -> "sita",
"re" -> "sire", "bi" -> "sibi", "am" -> "siam", "es" -> "sies",
"no" -> "sino", "nh" -> "sinh", "lv" -> "silv", "ts" -> "sits"|>,
"p" -> <|"h" -> "ph", "he" -> "phe", "li" -> "pli", "c" -> "pc", "n" -> "pn",
"o" -> "po", "f" -> "pf", "ne" -> "pne", "al" -> "pal", "si" -> "psi",
"p" -> "pp", "s" -> "ps", "ar" -> "par", "fe" -> "pfe", "as" -> "pas",
"se" -> "pse", "y" -> "py", "ru" -> "pru", "ag" -> "pag", "in" -> "pin",
"te" -> "pte", "i" -> "pi", "cs" -> "pcs", "la" -> "pla", "pm" -> "ppm",
"eu" -> "peu", "ho" -> "pho", "er" -> "per", "lu" -> "plu", "ta" -> "pta",
"re" -> "pre", "os" -> "pos", "ir" -> "pir", "au" -> "pau", "at" -> "pat",
"ra" -> "pra", "ac" -> "pac", "u" -> "pu", "am" -> "pam", "es" -> "pes",
"og" -> "pog"|>,
"s" -> <|"h" -> "sh", "he" -> "she", "li" -> "sli", "c" -> "sc", "n" -> "sn",
"o" -> "so", "ne" -> "sne", "na" -> "sna", "al" -> "sal", "p" -> "sp",
"s" -> "ss", "cl" -> "scl", "ar" -> "sar", "k" -> "sk", "ca" -> "sca",
"ti" -> "sti", "v" -> "sv", "cr" -> "scr", "co" -> "sco", "ni" -> "sni",
"cu" -> "scu", "as" -> "sas", "y" -> "sy", "mo" -> "smo", "ag" -> "sag",
"in" -> "sin", "te" -> "ste", "i" -> "si", "la" -> "sla", "ce" -> "sce",
"pr" -> "spr", "eu" -> "seu", "ho" -> "sho", "er" -> "ser", "yb" -> "syb",
"lu" -> "slu", "ta" -> "sta", "w" -> "sw", "os" -> "sos", "ir" -> "sir",
"au" -> "sau", "po" -> "spo", "at" -> "sat", "ac" -> "sac", "pa" -> "spa",
"u" -> "su", "pu" -> "spu", "am" -> "sam", "es" -> "ses", "no" -> "sno",
"og" -> "sog"|>,
"cl" -> <|"o" -> "clo", "ar" -> "clar", "as" -> "clas", "y" -> "cly",
"in" -> "clin", "i" -> "cli", "er" -> "cler", "os" -> "clos", "au" -> "clau",
"at" -> "clat", "ac" -> "clac", "u" -> "clu", "am" -> "clam",
"og" -> "clog"|>,
"ar" -> <|"li" -> "arli", "b" -> "arb", "c" -> "arc", "n" -> "arn",
"o" -> "aro", "ne" -> "arne", "al" -> "aral", "si" -> "arsi", "p" -> "arp",
"s" -> "ars", "ar" -> "arar", "k" -> "ark", "ca" -> "arca", "ti" -> "arti",
"ge" -> "arge", "se" -> "arse", "y" -> "ary", "mo" -> "armo", "rh" -> "arrh",
"ag" -> "arag", "te" -> "arte", "i" -> "ari", "cs" -> "arcs",
"ce" -> "arce", "ta" -> "arta", "re" -> "arre", "os" -> "aros",
"au" -> "arau", "tl" -> "artl", "bi" -> "arbi", "ra" -> "arra",
"ac" -> "arac", "th" -> "arth", "u" -> "aru", "am" -> "aram", "es" -> "ares",
"no" -> "arno", "nh" -> "arnh", "mc" -> "armc", "ts" -> "arts"|>,
"k" -> <|"h" -> "kh", "li" -> "kli", "c" -> "kc", "n" -> "kn", "o" -> "ko",
"ne" -> "kne", "na" -> "kna", "al" -> "kal", "p" -> "kp", "s" -> "ks",
"ar" -> "kar", "v" -> "kv", "ni" -> "kni", "as" -> "kas", "y" -> "ky",
"ru" -> "kru", "ag" -> "kag", "in" -> "kin", "i" -> "ki", "la" -> "kla",
"ho" -> "kho", "er" -> "ker", "lu" -> "klu", "w" -> "kw", "re" -> "kre",
"os" -> "kos", "ir" -> "kir", "au" -> "kau", "at" -> "kat", "ra" -> "kra",
"u" -> "ku", "am" -> "kam", "es" -> "kes", "no" -> "kno"|>,
"ca" -> <|"h" -> "cah", "li" -> "cali", "be" -> "cabe", "b" -> "cab",
"c" -> "cac", "n" -> "can", "f" -> "caf", "ne" -> "cane", "na" -> "cana",
"si" -> "casi", "p" -> "cap", "s" -> "cas", "k" -> "cak", "ca" -> "caca",
"sc" -> "casc", "ti" -> "cati", "v" -> "cav", "fe" -> "cafe", "co" -> "caco",
"ni" -> "cani", "ge" -> "cage", "se" -> "case", "br" -> "cabr",
"rb" -> "carb", "y" -> "cay", "nb" -> "canb", "mo" -> "camo", "tc" -> "catc",
"ru" -> "caru", "rh" -> "carh", "in" -> "cain", "te" -> "cate",
"i" -> "cai", "ba" -> "caba", "la" -> "cala", "pr" -> "capr", "nd" -> "cand",
"tb" -> "catb", "ho" -> "caho", "er" -> "caer", "lu" -> "calu",
"ta" -> "cata", "w" -> "caw", "re" -> "care", "ir" -> "cair", "pt" -> "capt",
"tl" -> "catl", "bi" -> "cabi", "po" -> "capo", "rn" -> "carn",
"ra" -> "cara", "th" -> "cath", "pa" -> "capa", "u" -> "cau", "pu" -> "capu",
"es" -> "caes", "md" -> "camd", "no" -> "cano", "rf" -> "carf",
"ds" -> "cads", "rg" -> "carg", "mc" -> "camc", "lv" -> "calv",
"ts" -> "cats"|>,
"sc" -> <|"h" -> "sch", "he" -> "sche", "o" -> "sco", "al" -> "scal",
"ar" -> "scar", "y" -> "scy", "ru" -> "scru", "in" -> "scin", "i" -> "sci",
"ho" -> "scho", "re" -> "scre", "at" -> "scat", "ra" -> "scra", "u" -> "scu",
"am" -> "scam"|>,
"ti" -> <|"li" -> "tili", "be" -> "tibe", "b" -> "tib", "c" -> "tic",
"n" -> "tin", "f" -> "tif", "ne" -> "tine", "na" -> "tina", "p" -> "tip",
"s" -> "tis", "ar" -> "tiar", "ti" -> "titi", "co" -> "tico", "ni" -> "tini",
"ge" -> "tige", "mo" -> "timo", "tc" -> "titc", "te" -> "tite",
"cs" -> "tics", "nd" -> "tind", "dy" -> "tidy", "er" -> "tier",
"tm" -> "titm", "ta" -> "tita", "re" -> "tire", "pt" -> "tipt",
"tl" -> "titl", "bi" -> "tibi", "ra" -> "tira", "th" -> "tith",
"np" -> "tinp", "es" -> "ties", "db" -> "tidb"|>,
"v" -> <|"h" -> "vh", "c" -> "vc", "o" -> "vo", "al" -> "val", "s" -> "vs",
"ar" -> "var", "cr" -> "vcr", "as" -> "vas", "y" -> "vy", "ag" -> "vag",
"in" -> "vin", "i" -> "vi", "la" -> "vla", "er" -> "ver", "hf" -> "vhf",
"ir" -> "vir", "au" -> "vau", "at" -> "vat", "ac" -> "vac", "u" -> "vu",
"am" -> "vam", "es" -> "ves", "hs" -> "vhs", "og" -> "vog"|>,
"cr" -> <|"o" -> "cro", "as" -> "cras", "y" -> "cry", "ag" -> "crag",
"in" -> "crin", "i" -> "cri", "yb" -> "cryb", "os" -> "cros", "at" -> "crat",
"ac" -> "crac", "u" -> "cru", "am" -> "cram", "es" -> "cres"|>,
"mn" -> <||>,
"fe" -> <|"li" -> "feli", "b" -> "feb", "c" -> "fec", "n" -> "fen",
"ne" -> "fene", "al" -> "feal", "s" -> "fes", "ar" -> "fear", "ca" -> "feca",
"ti" -> "feti", "v" -> "fev", "ni" -> "feni", "cu" -> "fecu",
"as" -> "feas", "br" -> "febr", "rb" -> "ferb", "y" -> "fey", "mo" -> "femo",
"tc" -> "fetc", "ru" -> "feru", "in" -> "fein", "te" -> "fete",
"i" -> "fei", "ce" -> "fece", "nd" -> "fend", "ta" -> "feta", "w" -> "few",
"tl" -> "fetl", "at" -> "feat", "rn" -> "fern", "ra" -> "fera", "u" -> "feu",
"es" -> "fees", "ds" -> "feds", "rg" -> "ferg"|>,
"co" -> <|"h" -> "coh", "he" -> "cohe", "li" -> "coli", "b" -> "cob",
"c" -> "coc", "n" -> "con", "o" -> "coo", "f" -> "cof", "ne" -> "cone",
"na" -> "cona", "al" -> "coal", "si" -> "cosi", "p" -> "cop", "s" -> "cos",
"ar" -> "coar", "k" -> "cok", "ca" -> "coca", "ti" -> "coti", "v" -> "cov",
"co" -> "coco", "ni" -> "coni", "ge" -> "coge", "as" -> "coas",
"se" -> "cose", "br" -> "cobr", "rb" -> "corb", "y" -> "coy", "mo" -> "como",
"ru" -> "coru", "ag" -> "coag", "in" -> "coin", "sb" -> "cosb",
"te" -> "cote", "i" -> "coi", "xe" -> "coxe", "ba" -> "coba", "la" -> "cola",
"pr" -> "copr", "nd" -> "cond", "sm" -> "cosm", "dy" -> "cody",
"ho" -> "coho", "er" -> "coer", "lu" -> "colu", "ta" -> "cota", "w" -> "cow",
"re" -> "core", "os" -> "coos", "ir" -> "coir", "pt" -> "copt",
"au" -> "coau", "at" -> "coat", "rn" -> "corn", "ra" -> "cora",
"ac" -> "coac", "pa" -> "copa", "u" -> "cou", "pu" -> "copu", "rf" -> "corf",
"mt" -> "comt", "ds" -> "cods", "rg" -> "corg", "ts" -> "cots"|>,
"ni" -> <|"h" -> "nih", "be" -> "nibe", "b" -> "nib", "c" -> "nic",
"n" -> "nin", "o" -> "nio", "f" -> "nif", "ne" -> "nine", "na" -> "nina",
"p" -> "nip", "s" -> "nis", "k" -> "nik", "ca" -> "nica", "co" -> "nico",
"ge" -> "nige", "se" -> "nise", "ag" -> "niag", "te" -> "nite",
"xe" -> "nixe", "ce" -> "nice", "ta" -> "nita", "re" -> "nire",
"ac" -> "niac", "am" -> "niam", "ts" -> "nits"|>,
"cu" -> <|"li" -> "culi", "be" -> "cube", "b" -> "cub", "c" -> "cuc",
"n" -> "cun", "f" -> "cuf", "ne" -> "cune", "na" -> "cuna", "p" -> "cup",
"s" -> "cus", "ti" -> "cuti", "v" -> "cuv", "cu" -> "cucu", "rb" -> "curb",
"in" -> "cuin", "te" -> "cute", "i" -> "cui", "ba" -> "cuba", "pr" -> "cupr",
"tb" -> "cutb", "ta" -> "cuta", "re" -> "cure", "tl" -> "cutl",
"pb" -> "cupb", "bi" -> "cubi", "po" -> "cupo", "ra" -> "cura",
"es" -> "cues", "rf" -> "curf", "ds" -> "cuds", "lv" -> "culv",
"ts" -> "cuts"|>,
"ga" -> <|"li" -> "gali", "b" -> "gab", "c" -> "gac", "n" -> "gan",
"o" -> "gao", "f" -> "gaf", "ne" -> "gane", "p" -> "gap", "s" -> "gas",
"sc" -> "gasc", "ti" -> "gati", "v" -> "gav", "cr" -> "gacr", "ga" -> "gaga",
"ge" -> "gage", "se" -> "gase", "br" -> "gabr", "rb" -> "garb",
"y" -> "gay", "mo" -> "gamo", "in" -> "gain", "sb" -> "gasb", "te" -> "gate",
"i" -> "gai", "ba" -> "gaba", "la" -> "gala", "nd" -> "gand",
"sm" -> "gasm", "lu" -> "galu", "w" -> "gaw", "re" -> "gare", "tl" -> "gatl",
"rn" -> "garn", "ra" -> "gara", "th" -> "gath", "u" -> "gau",
"rf" -> "garf", "ds" -> "gads", "rg" -> "garg", "lv" -> "galv",
"ts" -> "gats"|>,
"ge" -> <|"h" -> "geh", "he" -> "gehe", "li" -> "geli", "c" -> "gec",
"n" -> "gen", "o" -> "geo", "f" -> "gef", "ne" -> "gene", "na" -> "gena",
"s" -> "ges", "ar" -> "gear", "ni" -> "geni", "rb" -> "gerb", "y" -> "gey",
"mo" -> "gemo", "ru" -> "geru", "i" -> "gei", "la" -> "gela", "nd" -> "gend",
"ta" -> "geta", "w" -> "gew", "os" -> "geos", "ra" -> "gera",
"th" -> "geth", "es" -> "gees", "no" -> "geno", "ts" -> "gets",
"og" -> "geog"|>,
"as" -> <|"h" -> "ash", "he" -> "ashe", "be" -> "asbe", "b" -> "asb",
"c" -> "asc", "o" -> "aso", "si" -> "assi", "p" -> "asp", "s" -> "ass",
"k" -> "ask", "ti" -> "asti", "cr" -> "ascr", "co" -> "asco", "ga" -> "asga",
"se" -> "asse", "y" -> "asy", "in" -> "asin", "te" -> "aste", "i" -> "asi",
"la" -> "asla", "ce" -> "asce", "ho" -> "asho", "hf" -> "ashf",
"ta" -> "asta", "w" -> "asw", "hg" -> "ashg", "th" -> "asth", "pa" -> "aspa",
"u" -> "asu", "am" -> "asam"|>,
"se" -> <|"li" -> "seli", "b" -> "seb", "c" -> "sec", "n" -> "sen",
"o" -> "seo", "ne" -> "sene", "na" -> "sena", "al" -> "seal", "p" -> "sep",
"s" -> "ses", "cl" -> "secl", "ar" -> "sear", "ca" -> "seca", "v" -> "sev",
"cr" -> "secr", "co" -> "seco", "ni" -> "seni", "cu" -> "secu",
"ga" -> "sega", "as" -> "seas", "rb" -> "serb", "y" -> "sey", "mo" -> "semo",
"ru" -> "seru", "ag" -> "seag", "in" -> "sein", "i" -> "sei",
"xe" -> "sexe", "cs" -> "secs", "ba" -> "seba", "la" -> "sela",
"ce" -> "sece", "nd" -> "send", "tb" -> "setb", "er" -> "seer", "w" -> "sew",
"re" -> "sere", "pt" -> "sept", "po" -> "sepo", "at" -> "seat",
"ra" -> "sera", "ac" -> "seac", "th" -> "seth", "pa" -> "sepa", "u" -> "seu",
"pu" -> "sepu", "am" -> "seam", "es" -> "sees", "no" -> "seno",
"rf" -> "serf", "mt" -> "semt", "rg" -> "serg", "lv" -> "selv",
"ts" -> "sets"|>,
"br" -> <|"n" -> "brn", "o" -> "bro", "as" -> "bras", "y" -> "bry",
"ag" -> "brag", "in" -> "brin", "i" -> "bri", "os" -> "bros", "at" -> "brat",
"ac" -> "brac", "u" -> "bru", "am" -> "bram", "es" -> "bres",
"no" -> "brno", "og" -> "brog"|>,
"kr" -> <|"o" -> "kro", "as" -> "kras", "y" -> "kry", "in" -> "krin",
"i" -> "kri", "u" -> "kru", "am" -> "kram", "es" -> "kres", "og" -> "krog"|>,
"sr" -> <|"in" -> "srin", "i" -> "sri"|>,
"y" -> <|"o" -> "yo", "al" -> "yal", "p" -> "yp", "ar" -> "yar", "v" -> "yv",
"as" -> "yas", "in" -> "yin", "i" -> "yi", "pr" -> "ypr", "er" -> "yer",
"os" -> "yos", "at" -> "yat", "ac" -> "yac", "u" -> "yu", "am" -> "yam",
"es" -> "yes", "og" -> "yog"|>, "nb" -> <|"c" -> "nbc"|>,
"mo" -> <|"h" -> "moh", "he" -> "mohe", "li" -> "moli", "b" -> "mob",
"c" -> "moc", "n" -> "mon", "o" -> "moo", "ne" -> "mone", "na" -> "mona",
"p" -> "mop", "s" -> "mos", "sc" -> "mosc", "ti" -> "moti", "v" -> "mov",
"ni" -> "moni", "ga" -> "moga", "se" -> "mose", "rb" -> "morb",
"te" -> "mote", "i" -> "moi", "la" -> "mola", "nd" -> "mond", "ho" -> "moho",
"lu" -> "molu", "w" -> "mow", "re" -> "more", "os" -> "moos",
"ir" -> "moir", "tl" -> "motl", "bi" -> "mobi", "at" -> "moat",
"rn" -> "morn", "ra" -> "mora", "th" -> "moth", "u" -> "mou", "no" -> "mono",
"ds" -> "mods", "rg" -> "morg", "ts" -> "mots", "og" -> "moog"|>,
"tc" -> <|"h" -> "tch"|>,
"ru" -> <|"h" -> "ruh", "li" -> "ruli", "be" -> "rube", "b" -> "rub",
"c" -> "ruc", "n" -> "run", "f" -> "ruf", "ne" -> "rune", "na" -> "runa",
"p" -> "rup", "s" -> "rus", "k" -> "ruk", "ni" -> "runi", "se" -> "ruse",
"br" -> "rubr", "mo" -> "rumo", "in" -> "ruin", "i" -> "rui", "ba" -> "ruba",
"nd" -> "rund", "dy" -> "rudy", "ta" -> "ruta", "pt" -> "rupt",
"tl" -> "rutl", "bi" -> "rubi", "ra" -> "rura", "th" -> "ruth",
"es" -> "rues", "no" -> "runo", "ts" -> "ruts"|>,
"rh" -> <|"o" -> "rho", "y" -> "rhy", "in" -> "rhin", "i" -> "rhi",
"eu" -> "rheu", "os" -> "rhos", "u" -> "rhu", "es" -> "rhes"|>,
"ag" -> <|"h" -> "agh", "li" -> "agli", "n" -> "agn", "o" -> "ago",
"ne" -> "agne", "ar" -> "agar", "ni" -> "agni", "as" -> "agas",
"in" -> "agin", "i" -> "agi", "la" -> "agla", "er" -> "ager", "re" -> "agre",
"at" -> "agat", "ra" -> "agra", "u" -> "agu", "am" -> "agam",
"es" -> "ages", "no" -> "agno", "og" -> "agog"|>, "cd" -> <||>,
"in" -> <|"h" -> "inh", "he" -> "inhe", "b" -> "inb", "c" -> "inc",
"n" -> "inn", "o" -> "ino", "f" -> "inf", "ne" -> "inne", "na" -> "inna",
"al" -> "inal", "si" -> "insi", "p" -> "inp", "s" -> "ins", "cl" -> "incl",
"ar" -> "inar", "k" -> "ink", "ca" -> "inca", "sc" -> "insc", "ti" -> "inti",
"v" -> "inv", "cr" -> "incr", "fe" -> "infe", "co" -> "inco",
"ni" -> "inni", "cu" -> "incu", "ga" -> "inga", "ge" -> "inge",
"as" -> "inas", "se" -> "inse", "br" -> "inbr", "mo" -> "inmo",
"ru" -> "inru", "te" -> "inte", "i" -> "ini", "la" -> "inla", "ce" -> "ince",
"dy" -> "indy", "ho" -> "inho", "er" -> "iner", "ta" -> "inta",
"w" -> "inw", "au" -> "inau", "at" -> "inat", "fr" -> "infr", "ac" -> "inac",
"pa" -> "inpa", "u" -> "inu", "pu" -> "inpu", "am" -> "inam",
"es" -> "ines", "no" -> "inno", "fl" -> "infl"|>,
...
"u" -> "flu", "am" -> "flam", "es" -> "fles", "og" -> "flog"|>,
"mc" -> <|"b" -> "mcb", "c" -> "mcc", "n" -> "mcn", "f" -> "mcf",
"ne" -> "mcne", "na" -> "mcna", "p" -> "mcp", "cl" -> "mccl", "k" -> "mck",
"ca" -> "mcca", "v" -> "mcv", "cr" -> "mccr", "co" -> "mcco", "cu" -> "mccu",
"ge" -> "mcge", "br" -> "mcbr", "in" -> "mcin", "i" -> "mci",
"la" -> "mcla", "lu" -> "mclu"|>, "lv" -> <|"o" -> "lvo"|>,
"ts" -> <|"ar" -> "tsar", "i" -> "tsi", "w" -> "tsw", "u" -> "tsu"|>,
"og" -> <|"li" -> "ogli", "b" -> "ogb", "i" -> "ogi", "re" -> "ogre"|>|>


and we see there's a lot of patterns that will work. This is unsurprising, but just shows that the exhaustive search will be slow.

The final leaves are't all valid words, but are instead valid patterns.

We can fill out the entirety of the trie starting from og without too much work though:

fillTrie[classes[[-1]], classes, validStartQ, 4]

<|"li" -> <|"n" -> "oglin"|>,
"b" -> <|"o" -> <|"mo" -> <|"s" -> <|"h" -> "ogbomosh",
"ho" -> "ogbomosho"|>|>|>|>, "i" -> <|"lv" -> <|"y" -> "ogilvy"|>|>,
"re" -> <|"s" -> <|"s" -> <|"es" -> "ogresses"|>,
"se" -> <|"s" -> "ogresses"|>|>, "i" -> <|"s" -> <|"h" -> "ogreish"|>|>|>|>


You'll notice that we only bottom out here. There are valid words (ogre, ogress) that we've skipped. You'd need to write extra logic to pull those as you build that I don't really feel like doing since it'd require yet another word valid word check.

Or if you want to build iteratively, you can do that from the existing trie. Here we'll just add another level of depth to the existing one

wat3 =
Map[
fillTrie[#, classes, validStartQ, 0] &,
wat,
{2}
];


and here are some of the valid prefixes we get from that

pullLeaves[v_] :=
Which[
AssociationQ[v], Values[v],
StringQ[v], v,
True, Map[pullLeaves, v]
];

allPrefixes = Flatten@Nest[pullLeaves , wat3, 3];

RandomSample[allPrefixes, 100]

{"haci", "cindy", "lippm", "braces", "soni", "hepp", "liki", "rayo", "clui", \
"hamh", "prebe", "infeas", "erys", "smok", "ambu", "fremo", "hover", "copla", \
"luaus", "hess", "halog", "sinc", "viri", "pratin", "biki", "sires", "koal", \
"sach", "refec", "unini", "libre", "hooli", "ructi", "cowpa", "teami", "fow", \
"arcs", "reinco", "tabas", "vibr", "parana", "acycl", "cube", "scorb", "osier", \
"noses", "niob", "sou", "recros", "ashra", "scher", "cluin", "scuba", "basen", \
"refin", "inept", "inho", "novi", "reino", "atona", "auber", "inhos", "lithu", \
"where", "viti", "shc", "cringe", "clare", "viru", "stasi", "copu", "lays", \
"fiat", "nouga", "catere", "yond", "morav", "lushe", "albani", "seagu", \
"cruxe", "reatta", "hipb", "reauth", "sunb", "yacc", "cairn", "runs", "tabo", \
"neck", "serfs", "noos", "casi", "senio", "rawn", "altern", "orgas", "sixe", \
"ascen", "poisi"}


and to show why DictionaryLookup is so slow, here's one of the valid words it accepts from that list

DictionaryLookup["erys*", IgnoreCase -> True]

{"erysipelas"}


This is a very, very obscure word. It also takes HBO.

Finally, here's a way to do this only with classes you care about. We'll take only the elements that are commonly found in humans (for a given definition of common)

abundances =
QuantityMagnitude@
EntityValue[EntityClass["Element", All],
EntityProperty["Element", "HumanMolarAbundance"]];
organicElements = Pick[
classes, # > 1.*^-5 & /@ abundances
]

{"h", "b", "c", "n", "o", "f", "na", "mg", "al", "si", "p", "s", "cl", "k", \
"ca", "fe", "zn", "br", "rb", "sr"}


and the trie is pretty quick to build with such a restricted option set

humanTrie = fillTrie["", organicElements, validStartQ, 3];

humanPrefixes = Flatten@Nest[pullLeaves, Values@humanTrie, 3];

RandomSample[humanPrefixes, 100]

{"brno", "confe", "sons", "oks", "soho", "alfalf", "ksh", "feal", "knack", \
"napo", "feca", "bobc", "hons", "bocca", "canb", "occasi", "naps", "ocho", \
"ops", "nass", "calca", "fops", "obno", "conc", "obso", "caffe", "nabok", \
"canap", "opos", "psh", "kopp", "coos", "palsi", "nop", "nonh", "cobr", "nonn", \
"cloch", "nosin", "nanos", "nons", "saloo", "scabb", "sincl", "poss", "fn", \
"conk", "sock", "hobs", "falkn", "popc", "nao", "balal", "nook", "booh", "nca", \
"cobs", "sih", "sikhs", "coals", "offsi", "copp", "offs", "poon", "sinf", \
"sif", "sorbo", "cahoo", "koal", "nans", "obsc", "pfenn", "closin", "capp", \
"clona", "cak", "feck", "conf", "foss", "falsif", "chos", "phal", "bocc", \
"sops", "aln", "coho", "pock", "brons", "caph", "sobr", "knob", "sina", "oops", \
"nbc", "cosb", "calc", "bobo", "pozn", "bosh", "hbo"}
$$$$
`
• Amazing work! I wish I could accept both of the answers given the question. Great effort! Apr 4 at 14:55