Reading from file:
std::ifstream in(NameOfFile);
std::string word;
while (in >> word) //will stop when word can't be read. probably bad file or end of file
{
// do something with word
}
Find word with same first and last letter
if (word.front() == word.back())
{
// do something with word
}
Note this does not handle words with capitalized letters. It would not find «Mom». It will likely crash over empty words. Both have trivial fixes.
Assign word to array
array[index++] == word;
This assumes you want to advance the index after inserting into the array. Please note the program will behave poorly if the array is overfilled. If you are allowed to by the assignment, please consider using a std::vector
.
Sorting the array
std::sort(array, array + index);
This assumes you are allowed to use std::sort
. Again, if possible use a std::vector
in place of the array. index
is assumed to be the value of index from the adding example above after all of the adding has been done. If you are NOT allowed to use std::sort
ask another question. It’s a lengthy topic.
Words from Letters FAQ
What Words Can I Make With These Letters?
That is the, «To be, or not to be,» question of all word games. There are usually many words you can make. Sometimes, there won’t be many at all. Just remember, sometimes it pays to make a word with fewer than the maximum number of points possible because it sets you up better for your next turn.
What Is the Longest English Word?
In the Oxford English Dictionary, the longest word is FLOCCINAUCINIHILIPILIFICATION, which means «the act of deeming or estimating something as worthless. Other dictionaries contain the word PNEUMONOULTRAMICROSCOPICSILICOVOLCANOCONIOSIS, which is coal miners’ «black lung disease.»
If you think that’s bad, German is worse. The longest word in German is DONAUDAMPFSCHIFFFAHRTSELEKTRIZITÄTENHAUPTBETRIEBSWERKBAUUNTERBEAMTENGESELLSCHAFT. FLOCCI…, at 29 letters, and PNEUMONO …, at 45 letters, combined aren’t that long! None of these words will fit on a crossword game board. Scrabble boards are 15 letters across, so the longest word, which also doubles as the word worth the most points, possible is OXYBENPHUTAZONE. It requires eight letters already be placed, none of them on any of the three triples or the double-letter squares, and it scores 1,778 points. No one has ever achieved it.
How Do You Find Words With Letters Missing?
The best way is to use our tool. Of course, you should never use it while playing competitive games because it would count as looking up words in the Official Scrabble Player’s Dictionary prior to playing. But, if you wanted to ask yourself, «Which words from letters in my rack can I make?» during a friendly Words With Friends game, then that would not be bad form. Outside of competition, study as many words as you want using the website. It’s an invaluable training tool!
Last letter-first letter
You are encouraged to solve this task according to the task description, using any language you may know.
A certain children’s game involves starting with a word in a particular category. Each participant in turn says a word, but that word must begin with the final letter of the previous word. Once a word has been given, it cannot be repeated. If an opponent cannot give a word in the category, they fall out of the game.
For example, with «animals» as the category,
Child 1: dog Child 2: goldfish Child 1: hippopotamus Child 2: snake ...
- Task
Take the following selection of 70 English Pokemon names (extracted from Wikipedia’s list of Pokemon) and generate the/a sequence with the highest possible number of Pokemon names where the subsequent name starts with the final letter of the preceding name.
No Pokemon name is to be repeated.
audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask
Extra brownie points for dealing with the full list of 646 names.
11l[edit]
Translation of: Python
F order_words(words) DefaultDict[Char, Set[String]] byfirst L(word) words byfirst[word[0]].add(word) R byfirst F linkfirst(&byfirst; sofar) assert(!sofar.empty) V chmatch = sofar.last.last V options = byfirst[chmatch] - Set(sofar) I options.empty R sofar E V alternatives = options.map(word -> linkfirst(&@byfirst, @sofar [+] [word])) R max(alternatives, key' s -> s.len) F llfl(words) V byfirst = order_words(words) R max((words.map(word -> linkfirst(&@byfirst, [word]))), key' s -> s.len) V pokemon_str = ‘audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask’ V pokemon = pokemon_str.split((‘ ’, "n")) V l = llfl(pokemon) L(i) (0 .< l.len).step(8) print(l[i .< i + 8].join(‘ ’)) print(l.len)
machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino 23
Ada[edit]
with Ada.Containers.Indefinite_Vectors, Ada.Text_IO; procedure Lalefile is package Word_Vec is new Ada.Containers.Indefinite_Vectors (Index_Type => Positive, Element_Type => String); use type Word_Vec.Vector, Ada.Containers.Count_Type; type Words_Type is array (Character) of Word_Vec.Vector; procedure Read(Words: out Words_Type) is F: Ada.Text_IO.File_Type; begin Ada.Text_IO.Open(File => F, Name => "pokemon70.txt", Mode => Ada.Text_IO.In_File); loop declare Word: String := Ada.Text_IO.Get_Line(F); begin exit when Word = ""; Words(Word(Word'First)).Append(Word); end; end loop; exception when Ada.Text_IO.End_Error => null; end Read; procedure Write (List: Word_Vec.Vector; Prefix: String := " ") is Copy: Word_Vec.Vector := List; begin loop exit when Copy.Is_Empty; Ada.Text_IO.Put_Line(Prefix & Copy.First_Element); Copy.Delete_First; end loop; end Write; function Run(Start: Character; Words: Words_Type) return Word_Vec.Vector is Result: Word_Vec.Vector := Word_Vec.Empty_Vector; begin for I in Words(Start).First_Index .. Words(Start).Last_Index loop declare Word: String := Words(Start).Element(I); Dupl: Words_Type := Words; Alternative : Word_Vec.Vector; begin Dupl(Start).Delete(I); Alternative := Word & Run(Word(Word'Last), Dupl); if Alternative.Length > Result.Length then Result := Alternative; end if; end; end loop; return Result; end Run; W: Words_Type; A_Vector: Word_Vec.Vector; Best: Word_Vec.Vector := Word_Vec.Empty_Vector; begin Read(W); Ada.Text_IO.Put("Processing "); for Ch in Character range 'a' .. 'z' loop Ada.Text_IO.Put(Ch & ", "); A_Vector := Run(Ch, W); if A_Vector.Length > Best.Length then Best := A_Vector; end if; end loop; Ada.Text_IO.New_Line; Ada.Text_IO.Put_Line("Length of longest Path:" & Integer'Image(Integer(Best.Length))); Ada.Text_IO.Put_Line("One such path:"); Write(Best); end Lalefile;
Output:
Processing a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, Length of longest Path: 23 One such path: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
BaCon[edit]
Naive implementation showing the algorithm.
all$ = "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon " "cresselia croagunk darmanitan deino emboar emolga exeggcute gabite " "girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan " "kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine " "nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 " "porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking " "sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko " "tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask" SUB check(list$, rest$) LOCAL x FOR x = 1 TO AMOUNT(rest$) IF RIGHT$(list$, 1) = LEFT$(TOKEN$(rest$, x), 1) THEN check(APPEND$(list$, 0, TOKEN$(rest$, x)), DEL$(rest$, x)) NEXT IF AMOUNT(list$) > total THEN total = AMOUNT(list$) result$ = list$ END IF END SUB FOR z = 1 TO AMOUNT(all$) CALL check(TOKEN$(all$, z), DEL$(all$,z)) NEXT PRINT total, ": ", result$ PRINT NL$, "Speed: ", TIMER, " msecs."
23: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino Speed: 662734 msecs.
Optimized implementation. The idea is to quantify the equations.
all$ = "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon " "cresselia croagunk darmanitan deino emboar emolga exeggcute gabite " "girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan " "kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine " "nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 " "porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking " "sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko " "tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask" SPLIT all$ TO name$ SIZE MaxSize DECLARE start, end, used, result ARRAY MaxSize FOR y = 0 TO MaxSize-1 start[y] = ASC(LEFT$(name$[y], 1)) end[y] = ASC(RIGHT$(name$[y], 1)) used[y] = -1 NEXT FOR i = 0 TO MaxSize-1 used[i] = 0 CALL check(used, i, 1) used[i] = -1 NEXT PRINT maxtotal, ": "; FOR a = 0 TO maxtotal-1 FOR y = 0 TO MaxSize-1 IF result[y] = a THEN PRINT name$[y]," "; NEXT NEXT PRINT NL$, "Speed: ", TIMER, " msecs." SUB check(ya[], ultim, nr) LOCAL x FOR x = 0 TO MaxSize-1 IF end[ultim] = start[x] AND ya[x] = -1 THEN ya[x] = nr CALL check(ya, x, nr+1) ya[x] = -1 END IF IF nr > maxtotal THEN maxtotal = nr OPTION MEMTYPE long COPY ya TO result SIZE MaxSize END IF NEXT END SUB
23: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino Speed: 818 msecs.
BASIC256[edit]
dim names$(1) names$ = { "audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask" } global names$, lnames, index, maxlen, first, last maxlen = 0 lnames = names$[?]-1 dim index(names$[?]) dim first(names$[?]) dim last(names$[?]) for t = 0 to lnames index[t] = t last[t] = asc(right(names$[t],1)) first[t] = asc(left(names$[t],1)) next t # try each name as the first name on the list for t = 0 to lnames call swapindex(0,t) call downlevel(1) call swapindex(0,t) next t end subroutine downlevel(lev) #print n$[?] + " " + lev if lev <= lnames then for t = lev to lnames if last[index[lev-1]] = first[index[t]] then call swapindex(lev,t) if lev >= maxlen then maxlen = lev call showsolution(lev) end if call downlevel(lev+1) call swapindex(lev,t) end if next t end if end subroutine subroutine showsolution(l) print l+1; for t = 0 to l print " " + names$[index[t]]; next t print end subroutine subroutine swapindex(a, b) # swap element a and bin in the array index (used to swap names$) t = index[a] index[a] = index[b] index[b] = t end subroutine
Output:
2 bagon nosepass 3 bagon nosepass sableye 4 bagon nosepass sableye emboar 5 bagon nosepass sableye emboar registeel ... 23 machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune emboar relicanth haxorus simisear registeel landorus seaking girafarig gabite exeggcute emolga audino 23 machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune exeggcute emboar registeel landorus simisear relicanth haxorus seaking girafarig gabite emolga audino 23 machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune exeggcute emboar relicanth haxorus simisear registeel landorus seaking girafarig gabite emolga audino
BBC BASIC[edit]
DIM names$(69) names$() = "audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask" maxPathLength% = 0 maxPathLengthCount% = 0 maxPathExample$ = "" FOR i% = 0 TO DIM(names$(),1) SWAP names$(0), names$(i%) PROClastfirst(names$(), 1) SWAP names$(0), names$(i%) NEXT PRINT "Maximum length = " ; maxPathLength% PRINT "Number of solutions with that length = " ; maxPathLengthCount% PRINT "One such solution: " ' maxPathExample$ END DEF PROClastfirst(names$(), offset%) LOCAL i%, l% IF offset% > maxPathLength% THEN maxPathLength% = offset% maxPathLengthCount% = 1 ELSE IF offset% = maxPathLength% THEN; maxPathLengthCount% += 1 maxPathExample$ = "" FOR i% = 0 TO offset%-1 maxPathExample$ += names$(i%) + CHR$13 + CHR$10 NEXT ENDIF l% = ASCRIGHT$(names$(offset% - 1)) FOR i% = offset% TO DIM(names$(),1) IF ASCnames$(i%) = l% THEN SWAP names$(i%), names$(offset%) PROClastfirst(names$(), offset%+1) SWAP names$(i%), names$(offset%) ENDIF NEXT ENDPROC
Output:
Maximum length = 23 Number of solutions with that length = 1248 One such solution: machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune exeggcute emboar relicanth haxorus simisear registeel landorus seaking girafarig gabite emolga audino
Bracmat[edit]
Naive[edit]
( audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask : ?names & 0:?max & :?sequence & ( lalefile = done todo A M Z Length first . !arg:(!done.)&!done:?sequence | !arg:(.?todo) & ( !todo : ?A %@?M (?Z&lalefile$(!M.!A !Z)&~) | ) | !arg:(@(%:? @?first) ?:?done.?todo) & :?M & ( !todo : ?A @(%:!first ?:?M) ( ?Z & lalefile$(!M !done.!A !Z) & ~ ) | !M: & !done:? [?Length & !Length:>!max:?max & !done:?sequence | ) ) & lalefile$(.!names) & out$("Length:" !max "Sequence:" !sequence) );
Output (read from bottom to top):
Length: 23 Sequence: audino emolga exeggcute gabite girafarig seaking haxorus trapinch rufflet heatmor relicanth simisear nosepass darmanitan loudred registeel emboar kricketune yamask scrafty landorus petilil machamp
Optimized[edit]
Optimizations:
The whl
loop transforms the flat list of names to, conceptually, a search tree with nodes at three levels. The lowest level contains the names. The top level contains the word’s first letter and the second level contains its last letter. Words starting with a specific letter are all children of one single top node, speeding up search for candidates. Under a second level node all words have the same letter at the start and the same letter at the end. When looking for candidates it always suffices to take the first word and ignore the rest. This optimization eliminates all solutions that merely are the result of swapping pairs of words with the same begin and end. Notice that the tree is built using the ‘smart’ binary operators *
, ^
, +
and L
(logarithm). Bracmat uses the commutative, distributive and associative laws to transform expressions containing these operators to canonical forms that fit the requiremens of the search tree. For example, the words in the list sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue
end up in this tree:
s ^ ( (.e)L(sableye*scolipede) + (.g)Lseaking + (.k)Lspoink + (.n)Lsilcoon + (.o)Lsealeo + (.r)Lsimisear + (.x)Lsnorlax + (.y)L(scrafty*snivy*starly) ) * t ^ ( (.a)Ltirtouga + (.e)Ltyrogue + (.h)Ltrapinch + (.o)Ltreecko )
Another, less important, optimization is the way in which the last letter of a name is found, using the position pattern [
rather than the pattern that matches at most one letter, @
.
The optimized version is about 4.5 times faster than the naive version.
( audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask : ?names & 1:?newnames & whl ' ( !names:@(%?name:%@?first ? @?last) ?names & !first^(.!last)L!name*!newnames:?newnames ) & !newnames:?names & 0:?max & :?sequence & ( lalefile = done todo A M Z Length first , Ms a z last candidates . !arg:(!done.)&!done:?sequence | !arg:(.?todo) & ( !todo : ?A * %?first^?candidates * ( ?Z & !candidates : ?a + ?lastL(%?M*?Ms) + ( ?z & lalefile$(!M.!A*!first^(!a+!lastL!Ms+!z)*!Z) & ~ ) ) | ) | !arg:(@(%:? [-2 ?first) ?:?done.?todo) & :?M & ( !todo:?A*!first^%?candidates*?Z & !candidates : ?a + ?lastL(%?M*?Ms) + ( ?z & lalefile $ (!M !done.!A*!first^(!a+!lastL!Ms+!z)*!Z) & ~ ) | !M: & !done:? [?Length & !Length:>!max:?max & !done:?sequence | ) ) & lalefile$(.!names) & out$("Length:" !max "Sequence:" !sequence) );
Output (read from bottom to top):
Length: 23 Sequence: audino emolga kricketune yamask scrafty haxorus trapinch rufflet simisear landorus registeel heatmor relicanth emboar exeggcute gabite girafarig seaking nosepass darmanitan loudred petilil machamp
C[edit]
From the D version.
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <inttypes.h> typedef struct { uint16_t index; char last_char, first_char; } Ref; Ref* longest_path_refs; size_t longest_path_refs_len; Ref* refs; size_t refs_len; size_t n_solutions; const char** longest_path; size_t longest_path_len; /// tally statistics void search(size_t curr_len) { if (curr_len == longest_path_refs_len) { n_solutions++; } else if (curr_len > longest_path_refs_len) { n_solutions = 1; longest_path_refs_len = curr_len; memcpy(longest_path_refs, refs, curr_len * sizeof(Ref)); } // recursive search intptr_t last_char = refs[curr_len - 1].last_char; for (size_t i = curr_len; i < refs_len; i++) if (refs[i].first_char == last_char) { Ref aux = refs[curr_len]; refs[curr_len] = refs[i]; refs[i] = aux; search(curr_len + 1); refs[i] = refs[curr_len]; refs[curr_len] = aux; } } void find_longest_chain(const char* items[], size_t items_len) { refs_len = items_len; refs = calloc(refs_len, sizeof(Ref)); // enough space for all items longest_path_refs_len = 0; longest_path_refs = calloc(refs_len, sizeof(Ref)); for (size_t i = 0; i < items_len; i++) { size_t itemsi_len = strlen(items[i]); if (itemsi_len <= 1) exit(1); refs[i].index = (uint16_t)i; refs[i].last_char = items[i][itemsi_len - 1]; refs[i].first_char = items[i][0]; } // try each item as possible start for (size_t i = 0; i < items_len; i++) { Ref aux = refs[0]; refs[0] = refs[i]; refs[i] = aux; search(1); refs[i] = refs[0]; refs[0] = aux; } longest_path_len = longest_path_refs_len; longest_path = calloc(longest_path_len, sizeof(const char*)); for (size_t i = 0; i < longest_path_len; i++) longest_path[i] = items[longest_path_refs[i].index]; free(longest_path_refs); free(refs); } int main() { const char* pokemon[] = {"audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask"}; size_t pokemon_len = sizeof(pokemon) / sizeof(pokemon[0]); find_longest_chain(pokemon, pokemon_len); printf("Maximum path length: %un", longest_path_len); printf("Paths of that length: %un", n_solutions); printf("Example path of that length:n"); for (size_t i = 0; i < longest_path_len; i += 7) { printf(" "); for (size_t j = i; j < (i+7) && j < longest_path_len; j++) printf("%s ", longest_path[j]); printf("n"); } free(longest_path); return 0; }
Output:
Maximum path length: 23 Paths of that length: 1248 Example path of that length: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
Runtime: about 0.49 seconds, gcc compiler.
Approximate[edit]
For dealing with full list (646 names), here’s an approximate method. Names are restricted to begin and end with a lower case letter, so for example in my input file «porygon2» is written as «porygon-two». It finds some chains with 300-odd length for 646 names, and found a chain with 23 for the 70 names (by luck, that is), and since it’s basically a one-pass method, running time is next to none. C99 code.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define forall(i, n) for (int i = 0; i < n; i++) typedef struct edge { char s, e, *str; struct edge *lnk; } edge; typedef struct { edge* e[26]; int nin, nout, in[26], out[26];} node; typedef struct { edge *e, *tail; int len, has[26]; } chain; node nodes[26]; edge *names, **tmp; int n_names; /* add edge to graph */ void store_edge(edge *g) { if (!g) return; int i = g->e, j = g->s; node *n = nodes + j; g->lnk = n->e[i]; n->e[i] = g, n->out[i]++, n->nout++; n = nodes + i, n->in[j]++, n->nin++; } /* unlink an edge between nodes i and j, and return the edge */ edge* remove_edge(int i, int j) { node *n = nodes + i; edge *g = n->e[j]; if (g) { n->e[j] = g->lnk; g->lnk = 0; n->out[j]--, n->nout--; n = nodes + j; n->in[i]--; n->nin--; } return g; } void read_names() { FILE *fp = fopen("poke646", "rt"); int i, len; char *buf; edge *p; if (!fp) abort(); fseek(fp, 0, SEEK_END); len = ftell(fp); buf = malloc(len + 1); fseek(fp, 0, SEEK_SET); fread(buf, 1, len, fp); fclose(fp); buf[len] = 0; for (n_names = i = 0; i < len; i++) if (isspace(buf[i])) buf[i] = 0, n_names++; if (buf[len-1]) n_names++; memset(nodes, 0, sizeof(node) * 26); tmp = calloc(n_names, sizeof(edge*)); p = names = malloc(sizeof(edge) * n_names); for (i = 0; i < n_names; i++, p++) { if (i) p->str = names[i-1].str + len + 1; else p->str = buf; len = strlen(p->str); p->s = p->str[0] - 'a'; p->e = p->str[len-1] - 'a'; if (p->s < 0 || p->s >= 26 || p->e < 0 || p->e >= 26) { printf("bad name %s: first/last char must be lettern", p->str); abort(); } } printf("read %d namesn", n_names); } void show_chain(chain *c) { printf("%d:", c->len); for (edge * e = c->e; e || !putchar('n'); e = e->lnk) printf(" %s", e->str); } /* Which next node has most enter or exit edges. */ int widest(int n, int out) { if (nodes[n].out[n]) return n; int mm = -1, mi = -1; forall(i, 26) { if (out) { if (nodes[n].out[i] && nodes[i].nout > mm) mi = i, mm = nodes[i].nout; } else { if (nodes[i].out[n] && nodes[i].nin > mm) mi = i, mm = nodes[i].nin; } } return mi; } void insert(chain *c, edge *e) { e->lnk = c->e; if (!c->tail) c->tail = e; c->e = e; c->len++; } void append(chain *c, edge *e) { if (c->tail) c->tail->lnk = e; else c->e = e; c->tail = e; c->len++; } edge * shift(chain *c) { edge *e = c->e; if (e) { c->e = e->lnk; if (!--c->len) c->tail = 0; } return e; } chain* make_chain(int s) { chain *c = calloc(1, sizeof(chain)); /* extend backwards */ for (int i, j = s; (i = widest(j, 0)) >= 0; j = i) insert(c, remove_edge(i, j)); /* extend forwards */ for (int i, j = s; (i = widest(j, 1)) >= 0; j = i) append(c, remove_edge(j, i)); for (int step = 0;; step++) { edge *e = c->e; for (int i = 0; i < step; i++) if (!(e = e->lnk)) break; if (!e) return c; int n = 0; for (int i, j = e->s; (i = widest(j, 0)) >= 0; j = i) { if (!(e = remove_edge(i, j))) break; tmp[n++] = e; } if (n > step) { forall(i, step) store_edge(shift(c)); forall(i, n) insert(c, tmp[i]); step = -1; } else while (--n >= 0) store_edge(tmp[n]); } return c; } int main(void) { int best = 0; read_names(); forall(i, 26) { /* rebuild the graph */ memset(nodes, 0, sizeof(nodes)); forall(j, n_names) store_edge(names + j); /* make a chain from node i */ chain *c = make_chain(i); if (c->len > best) { show_chain(c); best = c->len; } free(c); } printf("longest found: %dn", best); return 0; }
output
read 646 names 307: voltorb breloom magikarp palpito... 308: voltorb bayleef forretress swinub b... 310: voltorb bayleef forretress sw... 312: voltorb breloom mandibuzz zek... 320: voltorb beldum mandibuzz zekrom m... 322: voltorb beldum mandibuzz zekrom murk... 323: voltorb breloom mandibuzz zekr... longest found: 323
C#[edit]
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { string pokemon_names = @"audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask"; string[] pokemon = pokemon_names.Split(new char[]{' ','n'}); List<string> chain = new List<string>(pokemon.Length); for (int i = 0; i < pokemon.Length; i++) { swap(ref pokemon[0], ref pokemon[i]); Search( pokemon, chain, 1 ); swap(ref pokemon[0], ref pokemon[i]); } foreach (string s in chain) Console.WriteLine(s); Console.ReadKey(); } static void Search(string[] pokemon, List<string> longest_chain, int len ) { if (len > longest_chain.Count) { longest_chain.Clear(); for (int i = 0; i < len; i++) longest_chain.Add(pokemon[i]); } char lastchar = pokemon[len - 1][pokemon[len-1].Length - 1]; for (int i = len; i < pokemon.Length; i++) { if (pokemon[i][0] == lastchar) { swap(ref pokemon[i], ref pokemon[len]); Search(pokemon, longest_chain, len + 1); swap(ref pokemon[i], ref pokemon[len]); } } } static void swap(ref string s1, ref string s2) { string tmp = s1; s1 = s2; s2 = tmp; } } }
machamp petilil landorus sableye emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus scrafty yamask kricketune exeggcute emolga audino
C++[edit]
#include <iostream> #include <string> #include <vector> struct last_letter_first_letter { size_t max_path_length = 0; size_t max_path_length_count = 0; std::vector<std::string> max_path_example; void search(std::vector<std::string>& names, size_t offset) { if (offset > max_path_length) { max_path_length = offset; max_path_length_count = 1; max_path_example.assign(names.begin(), names.begin() + offset); } else if (offset == max_path_length) { ++max_path_length_count; } char last_letter = names[offset - 1].back(); for (size_t i = offset, n = names.size(); i < n; ++i) { if (names[i][0] == last_letter) { names[i].swap(names[offset]); search(names, offset + 1); names[i].swap(names[offset]); } } } void find_longest_chain(std::vector<std::string>& names) { max_path_length = 0; max_path_length_count = 0; max_path_example.clear(); for (size_t i = 0, n = names.size(); i < n; ++i) { names[i].swap(names[0]); search(names, 1); names[i].swap(names[0]); } } }; int main() { std::vector<std::string> names{"audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask"}; last_letter_first_letter solver; solver.find_longest_chain(names); std::cout << "Maximum path length: " << solver.max_path_length << 'n'; std::cout << "Paths of that length: " << solver.max_path_length_count << 'n'; std::cout << "Example path of that length:n "; for (size_t i = 0, n = solver.max_path_example.size(); i < n; ++i) { if (i > 0 && i % 7 == 0) std::cout << "n "; else if (i > 0) std::cout << ' '; std::cout << solver.max_path_example[i]; } std::cout << 'n'; }
Maximum path length: 23 Paths of that length: 1248 Example path of that length: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
Clojure[edit]
(ns rosetta-code.last-letter-first-letter (:require clojure.string)) (defn by-first-letter "Returns a map from letters to a set of words that start with that letter" [words] (into {} (map (fn [[k v]] [k (set v)])) (group-by first words))) (defn longest-path-from "Find a longest path starting at word, using only words-by-first-letter for successive words. Returns a pair of [length list-of-words] to describe the path." [word words-by-first-letter] (let [words-without-word (update words-by-first-letter (first word) disj word) next-words (words-without-word (last word))] (if (empty? next-words) [1 [word]] (let [sub-paths (map #(longest-path-from % words-without-word) next-words) [length words-of-path] (apply max-key first sub-paths)] [(inc length) (cons word words-of-path)])))) (defn longest-word-chain "Find a longest path among the words in word-list, by performing a longest path search starting at each word in the list." [word-list] (let [words-by-letter (by-first-letter word-list)] (apply max-key first (pmap #(longest-path-from % words-by-letter) word-list)))) (defn word-list-from-file [file-name] (let [contents (slurp file-name) words (clojure.string/split contents #"[ n]")] (filter #(not (empty? %)) words))) (time (longest-word-chain (word-list-from-file "pokemon.txt")))
Evaluating the last line:
"Elapsed time: 2867.337816 msecs" [23 ("machamp" "pinsir" "relicanth" "heatmor" "registeel" "landorus" "seaking" "girafarig" "gabite" "exeggcute" "emboar" "rufflet" "trapinch" "haxorus" "simisear" "remoraid" "darmanitan" "nosepass" "scrafty" "yamask" "kricketune" "emolga" "audino")]
It initially ran in about 5 seconds, then I changed map
to pmap
(parallel map) in longest-word-chain
.
This gave a nice speedup for a dual core laptop; the speedup for parallel searches was over 3x on a server.
Common Lisp[edit]
Pretty brute force here. Takes a couple of seconds to run.
;;; return all the words that start with an initial letter (defun filter-with-init (words init) (remove-if-not (lambda (word) (eql init (aref word 0))) words)) ;;; produce a hash table whose key is the initial letter of a word and whose value is ;;; a list of the words that start with that initial letter (defun group-by-first-letter (words) (let ((map_letters (make-hash-table)) (inits (remove-duplicates (mapcar (lambda (word) (aref word 0)) words)))) (dolist (init inits map_letters) (setf (gethash init map_letters) (filter-with-init words init))) )) ;;; Get the last letter in a word or array (defun last-element (array) (aref array (- (length array) 1))) ;;; Produce a hash table whose key is a word and whose value is a list of the ;;; words that can follow that word (defun get-followers (words) (let ((map-word-to-followers (make-hash-table :test 'equal)) (init_hash (group-by-first-letter words))) (dolist (word words map-word-to-followers) (setf (gethash word map-word-to-followers) (gethash (last-element word) init_hash))))) ;;; Retrieve all the keys from a hash table (defun keys (hashtbl) (let ((allkeys ())) (maphash #'(lambda (key val) (setf allkeys (cons key allkeys))) hashtbl) allkeys)) ;;; Find the words which can follow a word and haven't been used yet. The parameters are: ;;; word - word being tested ;;; followers - the hash table returned from get-followers ;;; available - hash table with word as key and boolean indicating whether that word ;;; has been used previously as value (defun get-available-followers (word followers available) (if (null word) (keys followers) (remove-if-not #'(lambda (word) (gethash word available)) (gethash word followers)))) ;;; Find the best in a list using an arbitrary test (defun best (lst test) (let ((top (car lst))) (do ((rest (cdr lst) (cdr rest))) ((null rest) top) (if (funcall test (car rest) top) (setf top (car rest)))))) ;;; Find the best path in a list (defun best-list-path (paths) (best paths #'(lambda (path1 path2) (> (length path1) (length path2))))) ;;; Find the best path given all the supporting information we need (defun best-path-from-available (word followers available depth path available-followers) (let ((results (mapcar #'(lambda (new-word) (dfs-recurse new-word followers available (+ 1 depth) (cons word path))) available-followers))) (best-list-path results))) ;;; Recurse to find the best available path - the meat of the algorithm (defun dfs-recurse (word followers available depth path) (let ((ret)) ; Mark the word as unavailable (setf (gethash word available) nil) ; Find the longest path starting with word (let ((available-followers (get-available-followers word followers available))) (setf ret (if (null available-followers) (cons word path) (best-path-from-available word followers available depth path available-followers)))) ; Mark the word as available again (setf (gethash word available) t) ; Return our longest path ret)) ;;; Create the availability table (defun make-available-table (words) (let ((available (make-hash-table))) (dolist (word words available) (setf (gethash word available) t)))) ;;; Find the best path for a set of words (defun best-path (words) (let ((followers (get-followers words)) (available (make-available-table words))) (cdr (reverse (dfs-recurse nil followers available 0 nil))))) ;;; set up the words as a set of strings (setf *words* (mapcar #'symbol-name '(audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask))) (setf *path* (best-path *words*))
Output:
("MACHAMP" "PETILIL" "LANDORUS" "SCRAFTY" "YAMASK" "KRICKETUNE" "EMBOAR" "REGISTEEL" "LOUDRED" "DARMANITAN" "NOSEPASS" "SIMISEAR" "RELICANTH" "HEATMOR" "RUFFLET" "TRAPINCH" "HAXORUS" "SEAKING" "GIRAFARIG" "GABITE" "EXEGGCUTE" "EMOLGA" "AUDINO")
D[edit]
Simple Version[edit]
Modified from the Go version:
import std.stdio, std.algorithm, std.string; void trySwap(string[] items, ref string item, in size_t len, ref string[] longest) pure nothrow @safe { swap(items[len], item); search(items, len + 1, longest); swap(items[len], item); } void search(string[] items, in size_t len, ref string[] longest) pure nothrow @safe { if (len > longest.length) longest = items[0 .. len].dup; immutable lastChar = items[len - 1][$ - 1]; foreach (ref item; items[len .. $]) if (item[0] == lastChar) trySwap(items, item, len, longest); } void main() @safe { auto pokemon = "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask".split; string[] solution; foreach (ref name; pokemon) trySwap(pokemon, name, 0, solution); writefln("%-(%sn%)", solution); }
Output:
machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
The run-time is about 0.9 seconds with the dmd compiler and about 0.55 seconds with the ldc2 compiler.
Improved Version[edit]
With two small changes the code gets faster. Here the names are represented as in C (so swapping them means just swapping a pointer, instead of swapping a pointer+length as before), and during the computation their last char is swapped with their second char (so there’s no need to keep the string lengths around or use strlen).
import std.stdio, std.algorithm, std.string, std.array, std.conv; void search(immutable(char)*[] items, in int len, ref immutable(char)*[] longest) pure { if (len > longest.length) longest = items[0 .. len].dup; immutable lastChar = items[len - 1][1]; foreach (ref item; items[len .. $]) if (item[0] == lastChar) { swap(items[len], item); search(items, len + 1, longest); swap(items[len], item); } } void main() { static immutable(char)* prep(in string s) pure { assert(s.length > 1); auto sd = s.dup; swap(sd[1], sd[$ - 1]); return sd.toStringz; } static string unprep(immutable(char)* sd) pure { auto ms = sd.to!(char[]); swap(ms[1], ms[$ - 1]); return ms; } auto pokemon = "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask".split.map!prep.array; immutable(char)*[] solution; foreach (ref name; pokemon) { swap(pokemon[0], name); search(pokemon, 1, solution); swap(pokemon[0], name); } writefln("%-(%sn%)", solution.map!unprep); }
This leads to tight enough code for the foreach loop in the search function:
LBB0_4: movl (%esi), %eax movb 19(%esp), %cl cmpb %cl, (%eax) jne LBB0_6 movl (%edi,%ebx,4), %ecx movl %eax, (%edi,%ebx,4) movl %ecx, (%esi) movl %edi, 8(%esp) movl 48(%esp), %eax movl %eax, 4(%esp) movl 12(%esp), %eax movl %eax, (%esp) movl 20(%esp), %eax calll __D25last_letter_first_letter26searchFNaAPyaxiKAPyaZv subl $12, %esp movl (%edi,%ebx,4), %eax movl (%esi), %ecx movl %ecx, (%edi,%ebx,4) movl %eax, (%esi) LBB0_6: addl $4, %esi decl %ebp jne LBB0_4
The run-time is about 0.65 seconds with LDC2 compiler. The output is similar.
Faster Version[edit]
import std.stdio, std.algorithm, std.string, std.range, std.typecons; Tuple!(uint, string[]) findLongestChain(in string[] words) pure nothrow { static struct Pair { string word; bool unused; } uint nSolutions; void search(Pair[][] sequences, in size_t minHead, in string currWord, string[] currentPath, size_t currentPathLen, ref string[] longestPath) nothrow { currentPath[currentPathLen] = currWord; currentPathLen++; if (currentPathLen == longestPath.length) { nSolutions++; } else if (currentPathLen > longestPath.length) { nSolutions = 1; longestPath = currentPath[0 .. currentPathLen].dup; } // Recursive search. immutable size_t lastCharIndex = currWord[$ - 1] - minHead; if (lastCharIndex < sequences.length) foreach (ref pair; sequences[lastCharIndex]) if (pair.unused) { pair.unused = false; search(sequences, minHead, pair.word, currentPath, currentPathLen, longestPath); pair.unused = true; } } if (words.empty) typeof(return)(0, null); immutable heads = words.map!q{ a[0] }.array; immutable size_t minHead = reduce!min(heads[0], heads[1.. $].representation); immutable size_t maxHead = reduce!max(heads[0], heads[1.. $].representation); auto sequences = new Pair[][](maxHead - minHead + 1, 0); foreach (const word; words) sequences[word[0] - minHead] ~= Pair(word, true); auto currentPath = new string[words.length]; string[] longestPath; // Try each item as possible start. foreach (seq; sequences) foreach (ref pair; seq) { pair.unused = false; search(sequences, minHead, pair.word, currentPath, 0, longestPath); pair.unused = true; } return typeof(return)(nSolutions, longestPath); } void main() { auto pokemon = "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask".toLower.split; // Remove duplicates. pokemon.length -= pokemon.sort().uniq.copy(pokemon).length; const sol = pokemon.findLongestChain; writeln("Maximum path length: ", sol[1].length); writeln("Paths of that length: ", sol[0]); writeln("Example path of that length:"); writefln("%( %-(%s %)n%)", sol[1].chunks(7)); }
Maximum path length: 23 Paths of that length: 1248 Example path of that length: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
Runtime: about 0.20 seconds with dmd compiler, about 0.15 seconds with ldc2 compiler.
Alternative Version[edit]
Translation of: PicoLisp
import std.stdio, std.algorithm, std.array, std.typecons, std.container, std.range; auto findChain(in string[] seq) pure nothrow /*@safe*/ { const adj = seq .map!(item => tuple(item, seq .filter!(x => x[0] == item[$ - 1]) .array)) .assocArray; SList!string res; foreach (immutable item; adj.byKey) { void inner(in string it, SList!string lst) nothrow { lst.insertFront(it); if (lst[].walkLength > res[].walkLength) res = lst; foreach (immutable x; adj[it]) if (!lst[].canFind(x)) inner(x, lst); } inner(item, SList!string()); } return res.array.retro; } void main() /*@safe*/ { "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask".split.findChain.writeln; }
["machamp", "petilil", "landorus", "scrafty", "yamask", "kricketune", "emboar", "registeel", "loudred", "darmanitan", "nosepass", "simisear", "relicanth", "heatmor", "rufflet", "trapinch", "haxorus", "seaking", "girafarig", "gabite", "exeggcute", "emolga", "audino"]
Run-time is about 3.1 seconds with ldc2 compiler.
Delphi[edit]
Visual implementation, this unit is a VCL Form with a Memo, a Button, a Checkbox, a DataGrid, a DBMemo, a DataSource and a ClientDataSet with tree fields (length integer,count integer,list memo):
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DBCtrls, DB, DBClient, Grids, DBGrids, ExtCtrls; type TLastLFirstL = class(TForm) Panel1: TPanel; Button1: TButton; Memo1: TMemo; DataSource1: TDataSource; ClientDataSet1: TClientDataSet; ClientDataSet1Longitud: TIntegerField; ClientDataSet1Cantidad: TIntegerField; ClientDataSet1Lista: TMemoField; Panel2: TPanel; DBMemo1: TDBMemo; DBGrid1: TDBGrid; Splitter1: TSplitter; CheckBox1: TCheckBox; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } FPokemons:TStrings; //internal list of words, taken from memo FIndex:TStrings; //index of words, based on starting letter FCurrList:TStrings; //current list of words being made FMax:integer; //max length of list found so far FCount:array of array[boolean]of integer; //counting of lists length ocurrences protected procedure BuildIndex; //build FIndex based on FPokemons contents procedure ClearIndex; //empty FIndex procedure PokeChain(starting:Char;mylevel:integer); //recursive procedure that builds words lists procedure BuildChains; //starts the lists building, by calling PokeChain for every FPokemons procedure AddCurrList; //called each time a list is "finished" (no more words to add to it) public { Public declarations } end; var LastLFirstL: TLastLFirstL; implementation {$R *.dfm} { TForm1 } { if the actual list is the longest found so far it is added to the dataset, otherwise its ocurrence is just counted} procedure TLastLFirstL.AddCurrList; var i,cc: integer; foundit:boolean; begin with ClientDataSet1 do begin cc := FCurrList.Count; if cc <= FMax then begin //count it foundit := false; for i := 0 to High(FCount) do begin foundit := FCount[i][false] = cc; if foundit then begin FCount[i][true] := FCount[i][true] + 1; break; end; end; if not foundit then begin //length that we never add to the dataset i := High(FCount); SetLength(FCount,i+2); Inc(i); FCount[i][false] := cc; FCount[i][true] := 1; end; exit; end; //new longest list is FCurrList, add it to the dataset FMax := cc; SetLength(FCount,High(Fcount)+2); //make room for ocurrence count FCount[High(FCount)][false] := cc; FCount[High(FCount)][true] := 1; //actual dataset adding Append; Fields[0].AsInteger := cc; Fields[1].AsInteger := 0; Fields[2].AsString := FCurrList.Text; //first one is example one Post; end; end; {} procedure TLastLFirstL.BuildChains; var stSeen:array of array[boolean] of char; poke:string; i:integer; tc:int64; filteqs:boolean; k: Integer; begin //do some cleaning before starting while not ClientDataSet1.IsEmpty do ClientDataSet1.Delete; Finalize(FCount); FMax := 0; filteqs := CheckBox1.Checked; //measure time tc := gettickcount; //each word is given the opportunity of starting a list if filteqs then begin //ignore words with same start and end as others already seen filteqs := False; for i := 0 to FPokemons.Count - 1 do begin poke := FPokemons[i]; for k := 0 to High(stSeen) do begin filteqs := (stSeen[k][false] = poke[1]) and (stSeen[k][true] = poke[length(poke)]); if filteqs then break; end; if filteqs then //already seen equivalent continue; FPokemons.Objects[i] := Pointer(1); FCurrList.Clear; //new list of words FCurrList.Add(poke); PokeChain(poke[length(poke)],2); //continue the list //register as seen, for future equivalents k := High(stSeen); SetLength(stSeen,k+2); Inc(k); stSeen[k][false] := poke[1]; stSeen[k][true] := poke[length(poke)]; FPokemons.Objects[i] := nil; end; Finalize(stSeen); end else begin for i := 0 to FPokemons.Count - 1 do begin poke := FPokemons[i]; FPokemons.Objects[i] := Pointer(1); FCurrList.Clear; //new list of words FCurrList.Add(poke); PokeChain(poke[length(poke)],2); //continue the list FPokemons.Objects[i] := nil; end; end; tc := gettickcount - tc; //don't consider dataset counting as part of the process //set actual counting of ocurrences on the dataset for i := 0 to High(FCount) do with ClientDataSet1 do begin if Locate('Longitud',FCount[i][false],[]) then Edit else begin Append; Fields[0].AsInteger := FCount[i][false]; Fields[2].AsString := 'No example preserved'; end; Fields[1].AsInteger := FCount[i][true]; Post; end; ClientDataSet1.IndexFieldNames := 'Longitud'; //show time taken Panel1.Caption := IntToStr(tc div 1000) + '.' + IntToStr(tc - (tc div 1000) * 1000) + ' segs.'; end; { builds an index based on the first letter of every word in consideration, because all we care about is the first and the last letter of every word. The index is a TStrings where each element is the starting letter and the corresponding object is a TList with all the indices of the words that starts with that letter. } procedure TLastLFirstL.BuildIndex; var i,ii: Integer; poke:string; st,ed:char; List:TList; k: Integer; found:boolean; begin ClearIndex; //just in case is not the first execution if not Assigned(FIndex) then // just in case IS the first execution FIndex := TStringList.Create; for i := 0 to FPokemons.Count - 1 do begin poke := FPokemons[i]; st := poke[1]; ed := poke[Length(poke)]; ii := FIndex.IndexOf(st); if ii<0 then //first time we see this starting letter ii := FIndex.AddObject(st,TList.Create); List := TList(FIndex.Objects[ii]); found := false; if CheckBox1.Checked then begin //ignore equivalent words (same start, same end) //all the List are words with the same start, so lets check the end for k := 0 to List.Count - 1 do begin poke := FPokemons[integer(List[k])]; found := poke[Length(poke)] = ed; if found then break; end; end; if not found then // not checking equivalents, or firts time this end is seen List.Add(Pointer(i)); end; end; { do your thing! } procedure TLastLFirstL.Button1Click(Sender: TObject); begin Panel1.Caption := 'Calculating..'; FPokemons.Assign(Memo1.Lines); //words in the game BuildIndex; BuildChains; end; { frees all the TList used by the index, clears the index } procedure TLastLFirstL.ClearIndex; var i:integer; begin if not Assigned(FIndex) then exit; for i := 0 to FIndex.Count - 1 do begin TList(FIndex.Objects[i]).Free; end; FIndex.Clear; end; procedure TLastLFirstL.FormCreate(Sender: TObject); begin FPokemons := TStringList.Create; FCurrList := TStringList.Create; end; procedure TLastLFirstL.FormDestroy(Sender: TObject); begin FCurrList.Free; FPokemons.Free; ClearIndex; //IMPORTANT! FIndex.Free; end; {where the magic happens. Recursive procedure that adds a word to the current list of words. Receives the starting letter of the word to add, and the "position" of the word in the chain. The position is used to ensure a word is not used twice for the list. } procedure TLastLFirstL.PokeChain(starting: Char;mylevel:integer); var i,ii,plevel:integer; List:TList; didit:boolean; begin application.processMessages; //don't let the interface die.. didit := False; //if we can't add another word, then we have reached the maximun length for the list ii := FIndex.IndexOf(starting); if ii >= 0 then begin //there are words with this starting letter List := TList(FIndex.Objects[ii]); for i := 0 to List.Count - 1 do begin ii := integer(List[i]); plevel := integer(FPokemons.Objects[ii]); // if the integer stored in the Object property is lower than mylevel, then this word is already in the list if (plevel > mylevel) or (plevel = 0) then begin // you can use the word //a try finally would be a good thing here, but... FCurrList.Add(FPokemons[ii]); //add the word to the list FPokemons.Objects[ii] := Pointer(mylevel); //signal is already in the list PokeChain(FPokemons[ii][length(FPokemons[ii])],mylevel+1); //add more words to the list FcurrList.Delete(FCurrList.Count-1); //already did my best, lets try with another word FPokemons.Objects[ii] := nil; //unsignal it, so it can be used "later" didit := True; //we did add one word to the list end; end; end; if not didit then //there is no way of making the list longer, process it AddCurrList; end; end.
Runtime varies depending if you run the «optimized» version or not. Ranges from 6 to 18 seconds.
NOTE: «optimized» version is actually a different algorithm, but in most cases returns the same results.
Elixir[edit]
Translation of: Ruby
defmodule LastLetter_FirstLetter do def search(names) do first = Enum.group_by(names, &String.first/1) sequences = Enum.reduce(names, [], fn name,acc -> add_name(first, acc, [name]) end) max = Enum.max_by(sequences, &length/1) |> length max_seqs = Enum.filter(sequences, fn seq -> length(seq) == max end) IO.puts "there are #{length(sequences)} possible sequences" IO.puts "the longest is #{max} names long" IO.puts "there are #{length(max_seqs)} such sequences. one is:" hd(max_seqs) |> Enum.with_index |> Enum.each(fn {name, idx} -> :io.fwrite " ~2w ~s~n", [idx+1, name] end) end defp add_name(first, sequences, seq) do last_letter = String.last(hd(seq)) potentials = Map.get(first, last_letter, []) -- seq if potentials == [] do [Enum.reverse(seq) | sequences] else Enum.reduce(potentials, sequences, fn name, acc -> add_name(first, acc, [name | seq]) end) end end end names = ~w( audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask ) LastLetter_FirstLetter.search(names)
there are 2076396 possible sequences the longest is 23 names long there are 1248 such sequences. one is: 1 machamp 2 petilil 3 landorus 4 scrafty 5 yamask 6 kricketune 7 emboar 8 registeel 9 loudred 10 darmanitan 11 nosepass 12 simisear 13 relicanth 14 heatmor 15 rufflet 16 trapinch 17 haxorus 18 seaking 19 girafarig 20 gabite 21 exeggcute 22 emolga 23 audino
Erlang[edit]
This is a parallel version. It takes 2.1 seconds. The (commented out) serial version takes 7.1 seconds. Both times measured on my (low end) Mac Mini. I thought parallel code would help in getting the brownie points. But even a small increase to 100 Pokemons makes the code run for more than the few spare hours I have in the evening.
-module( last_letter_first_letter ). -export( [solve/1, task/0] ). solve( Names ) -> Dict = lists:foldl( fun dict_append/2, dict:new(), Names ), Chains = construct_chains_in_parallel( Dict ), % Chains = [construct_chain_from_key(Dict, X) || X <- dict:fetch_keys(Dict)], lists:foldl( fun construct_chain_longest/2, [], Chains ). task() -> solve( binary:split(names(), <<" ">>, [global]) ). construct_chains_in_parallel( Dict ) -> My_pid = erlang:self(), Pids = [erlang:spawn( fun() -> My_pid ! {erlang:self(), construct_chain_from_key(Dict, X)} end) || X <- dict:fetch_keys(Dict)], [receive {X, Chain} -> Chain end || X <- Pids]. construct_chain_from_key( Dict, First_letter ) -> Names = construct_chain_names( dict:find(First_letter, Dict) ), construct_chain_from_names( Names, Dict, [] ). construct_chain_from_names( [], _Dict, Best_chain ) -> Best_chain; construct_chain_from_names( [{Name, Last_letter} | T], Dict, Best_chain ) -> New_dict = dict_delete( Name, Dict ), New_chain = [Name | construct_chain_from_key( New_dict, Last_letter )], construct_chain_from_names( T, Dict, construct_chain_longest(Best_chain, New_chain) ). construct_chain_longest( Chain1, Chain2 ) when length(Chain1) > length(Chain2) -> Chain1; construct_chain_longest( _Chain1, Chain2 ) -> Chain2. construct_chain_names( {ok, {Name, Last_letter}} ) -> [{Name, Last_letter}]; construct_chain_names( {ok, Values} ) -> Values; construct_chain_names( error ) -> []. dict_append( Name, Acc ) -> {First_letter, {Name, Last_letter}} = dict_item( Name ), dict:append( First_letter, {Name, Last_letter}, Acc ). dict_item( <<First_letter, _T/binary>>=Name ) -> Until_last_letter = erlang:byte_size( Name ) - 1, <<_H:Until_last_letter/binary, Last_letter>> = Name, {First_letter, {Name, Last_letter}}. dict_delete( <<First_letter, _T/binary>>=Name, Dict ) -> Name_last_letters = dict:fetch(First_letter, Dict), dict:store( First_letter, lists:keydelete(Name, 1, Name_last_letters), Dict ). names() -> <<"audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask">>.
11> last_letter_first_letter:task(). [<<"machamp">>,<<"pinsir">>,<<"rufflet">>,<<"trapinch">>, <<"heatmor">>,<<"remoraid">>,<<"darmanitan">>, <<"nosepass">>,<<"starly">>,<<"yamask">>,<<"kricketune">>, <<"exeggcute">>,<<"emboar">>,<<"relicanth">>,<<"haxorus">>, <<"simisear">>,<<"registeel">>,<<"landorus">>,<<"seaking">>, <<"girafarig">>,<<"gabite">>,<<"emolga">>,<<"audino">>]
Go[edit]
Depth first, starting with each possible name.
package main import ( "fmt" "strings" ) var pokemon = `audino bagon baltoy...67 names omitted...` func main() { // split text into slice representing directed graph var d []string for _, l := range strings.Split(pokemon, "n") { d = append(d, strings.Fields(l)...) } fmt.Println("searching", len(d), "names...") // try each name as possible start for i := range d { d[0], d[i] = d[i], d[0] search(d, 1, len(d[0])) d[0], d[i] = d[i], d[0] } fmt.Println("maximum path length:", len(ex)) fmt.Println("paths of that length:", nMax) fmt.Print("example path of that length:") for i, n := range ex { if i%6 == 0 { fmt.Print("n ") } fmt.Print(n, " ") } fmt.Println() } var ex []string var nMax int func search(d []string, i, ncPath int) { // tally statistics if i == len(ex) { nMax++ } else if i > len(ex) { nMax = 1 ex = append(ex[:0], d[:i]...) } // recursive search lastName := d[i-1] lastChar := lastName[len(lastName)-1] for j := i; j < len(d); j++ { if d[j][0] == lastChar { d[i], d[j] = d[j], d[i] search(d, i+1, ncPath+1+len(d[i])) d[i], d[j] = d[j], d[i] } } }
Output:
searching 70 names... maximum path length: 23 paths of that length: 1248 example path of that length: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
Haskell[edit]
Note: This takes ~80 seconds to complete on my machine.
import Data.List import qualified Data.ByteString.Char8 as B allPokemon :: [B.ByteString] allPokemon = map B.pack $ words "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask" growChains :: [[B.ByteString]] -> [B.ByteString] growChains pcs | nextChainSet == [] = head pcs | otherwise = growChains nextChainSet where nextChainSet = pcs >>= findLinks findLinks pc = map (x -> pc ++ [x]) $ filter (isLink $ last pc) (allPokemon \ pc) isLink pl pr = B.last pl == B.head pr main = mapM_ B.putStrLn $ growChains $ map (x -> [x]) allPokemon
Output:
machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
A simpler version (no ByteString), about 2.4 times slower (GHC -O3), same output:
import Data.List allPokemon = words "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask" growChains :: [[String]] -> [String] growChains pcs | nextChainSet == [] = head pcs | otherwise = growChains nextChainSet where nextChainSet = pcs >>= findLinks findLinks pc = map (x -> pc ++ [x]) $ filter (isLink $ last pc) (allPokemon \ pc) isLink pl pr = last pl == head pr main = mapM_ putStrLn $ growChains $ map (x -> [x]) allPokemon
Icon and Unicon[edit]
Works in both languages (brute force):
global words procedure main() words := table() every word := genwords(&input) do { /words[word[1]] := [] put(words[word[1]], word) } bP := [] every p := getPath(!!words,[]) do if *p > *bP then bP := copy(p) write("Longest: ",*bP) every writes((!bP||" ")|"n") end procedure getPath(word, p) if word == !p then return p if /words[word[-1]] then suspend p <- p ||| [word] else suspend getPath(!words[word[-1]], p <- p ||| [word]) end procedure genwords(f) while l := !f do l ? while tab(upto(&letters)) do suspend tab(many(&letters))1 end
Sample run on sample data:
->llfl <llfl.in Longest: 23 machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino ->
J[edit]
Here, we use a brute force breadth-first search. Unless we know ahead of time how long «longest» is, we must try all possibilities to ensure that an unchecked possibility is not longer than a possibility which we have found.
pokenames=: ;:0 :0-.LF audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask ) seqs=: 3 :0 links=. <@I. _1 =/&({&>&y) 0 next=. ,.i.#links while.#next do. r=. next assert. 1e9>*/8,$r next=. (#~ (-: ~.)"1) >;<@(] <@,"1 0 links {::~ {:)"1 r end. r )
The line assert. 1e9>*/8,$r
was added to avoid a very bad behavior from microsoft windows which appeared on different arguments, when intermediate results became too large (the machine would have to be rebooted when intermediate results became an order of magnitude larger than the available physical memory). By ensuring that the program would end before consuming that much virtual memory, this behavior from the operating system can be avoided. Note that 9!:21 and/or 9!:33 could also be used to prevent OS instability triggered by requesting too many resources.
With this procedure we are able to conduct the entire search for this list of names:
$R=: seqs pokenames 1248 23
With this data set, we have 1248 sequences of names which have the longest possible length, and those sequences are 23 names long. Here’s one of them:
>pokenames {~{.R machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
Java[edit]
// derived from C final class LastLetterFirstLetter { static int maxPathLength = 0; static int maxPathLengthCount = 0; static final StringBuffer maxPathExample = new StringBuffer(500); static final String[] names = {"audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask"}; static void recursive(String[] part, int offset) { if (offset > maxPathLength) { maxPathLength = offset; maxPathLengthCount = 1; } else if (offset == maxPathLength) { maxPathLengthCount++; maxPathExample.setLength(0); for (int i = 0; i < offset; i++) { maxPathExample.append((i % 5 == 0 ? "n " : " ")); maxPathExample.append(part[i]); } } final char lastChar = part[offset - 1].charAt(part[offset - 1].length()-1); for (int i = offset; i < part.length; i++) { if (part[i].charAt(0) == lastChar) { String tmp = names[offset]; names[offset] = names[i]; names[i] = tmp; recursive(names, offset+1); names[i] = names[offset]; names[offset] = tmp; } } } public static void main(String[] args) { for (int i = 0; i < names.length; i++) { String tmp = names[0]; names[0] = names[i]; names[i] = tmp; recursive(names, 1); names[i] = names[0]; names[0] = tmp; } System.out.println("maximum path length : " + maxPathLength); System.out.println("paths of that length : " + maxPathLengthCount); System.out.println("example path of that length:" + maxPathExample); } }
Output:
maximum path length : 23 paths of that length : 1248 example path of that length: machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune exeggcute emboar relicanth haxorus simisear registeel landorus seaking girafarig gabite emolga audino
JavaScript[edit]
(Required for the process object)
/** * Find the letter the word ends on * @param {string} word * @returns {string} */ const endsWith = word => word[word.length - 1]; /** * Remove the used elements from the candidate elements * @param {Array<string>} words Candidate words * @param {Array<string>} used Used words * @returns {*} */ const getCandidates = (words, used) => words.filter(e => !used.includes(e)); /** * Build a map of letters to words that start with that letter * @param {Array<string>} words * @returns {Map<string, Array<string>>} */ const buildLookup = words => { const lookup = new Map(); words.forEach(e => { const start = e[0]; lookup.set(start, [...(lookup.get(start) || []), e]); }); return lookup; }; /** * Main function * @param {Array<string>} names */ const findPaths = names => { const t0 = process.hrtime(); console.log('Checking:', names.length, 'names'); const lookup = buildLookup(names); let maxNum = 0; let maxPaths = []; const parseResult = arr => { if (typeof arr[0] === 'object') { arr.forEach(el => parseResult(el)) } else { if (arr.length > maxNum) { maxNum = arr.length; maxPaths = [arr]; } if (arr.length === maxNum) { maxPaths.push(arr) } } }; const searchWords = (word, res) => { const cs = getCandidates(lookup.get(endsWith(word)) || [], res); return cs.length ? cs.map(e => searchWords(e, [...res, e])) : res; }; names.forEach(word => { const res = searchWords(word, [word]); parseResult(res); }); const t1 = process.hrtime(t0); console.info('Execution time (hr): %ds %dms', t1[0], t1[1] / 1000000); console.log('Max Path:', maxNum); console.log('Matching Paths:', maxPaths.length); console.log('Example Path:', maxPaths[0]); }; const pokimon = ["audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask"]; findPaths(pokimon);
Checking: 70 names Execution time (hr): 2s 121.778223ms Max Path: 23 Matching Paths: 1249 Example Path: [ 'machamp', 'petilil', 'landorus', 'scrafty', 'yamask', 'kricketune', 'emboar', 'registeel', 'loudred', 'darmanitan', 'nosepass', 'simisear', 'relicanth', 'heatmor', 'rufflet', 'trapinch', 'haxorus', 'seaking', 'girafarig', 'gabite', 'exeggcute', 'emolga', 'audino' ]
jq[edit]
Short and sweet, with a nod to efficiency in the form of a dictionary for lookup of still-available words.
jq’s «debug» filter is used to illustrate how progress can be monitored.
It writes to stderr. If your jq does not include «debug», simply remove or comment-out the entire line.
Utility functions:
# convert a list of unique words to a dictionary def dictionary: reduce .[] as $word ({}; .[$word[0:1]] += [$word]) ; # remove "word" from the input dictionary assuming the key is already there: def remove(word): .[word[0:1]] -= [word];
The last-letter/first-letter game:
# left-right admissibility def admissible: .[0][-1:] == .[1][0:1]; # input: [word, dictionary_of_available_words_excluding_word] # output: a (possibly empty) stream of admissible values: [next_word, updated_dictionary], # where next_word can follow the given word. def next: .[0] as $word | if $word == null then empty else .[1] as $dictionary | $word[-1:] as $last | (($dictionary[$last] // []) | .[]) as $next | [ $next, ($dictionary | remove($next)) ] end ; # Produce an array representing a thread starting at "word": # Input: [word, dictionary_of_available_words] def thread: if .[1] == [] then [ .[0] ] else (next // null) as $next | [.[0]] + (if $next then ($next | thread) else [] end) end ; # Input: list of words # Output: [ maximal_length, maximal_thread] def maximal: def maximum(start): . as $dictionary | reduce ( [start, ($dictionary | remove(start))] | thread ) as $thread ([0, null]; ($thread|length) as $l | if $l > .[0] then [$l, $thread] else . end ); dictionary as $dictionary | reduce .[] as $name ( [0,null]; ($dictionary | maximum($name)) as $ans # If your jq does not include "debug", simply remove or comment-out the following line: | ([$name, $ans[0]] | debug) as $debug | if $ans[0] > .[0] then $ans else . end );
Example:
def names: ["audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask" ] ; names | maximal
(scrollable)
The «DEBUG» lines are included to illustrate how progress can be monitored. They show the maximal length for the indicated initial word.
$ jq -n -f Last_letter-first_letter.jq ["DEBUG:",["audino",1]] ["DEBUG:",["bagon",20]] ["DEBUG:",["baltoy",21]] ["DEBUG:",["banette",20]] ["DEBUG:",["bidoof",1]] ["DEBUG:",["braviary",21]] ["DEBUG:",["bronzor",22]] ["DEBUG:",["carracosta",2]] ["DEBUG:",["charmeleon",20]] ["DEBUG:",["cresselia",2]] ["DEBUG:",["croagunk",21]] ["DEBUG:",["darmanitan",20]] ["DEBUG:",["deino",1]] ["DEBUG:",["emboar",19]] ["DEBUG:",["emolga",2]] ["DEBUG:",["exeggcute",19]] ["DEBUG:",["gabite",20]] ["DEBUG:",["girafarig",20]] ["DEBUG:",["gulpin",20]] ["DEBUG:",["haxorus",20]] ["DEBUG:",["heatmor",20]] ["DEBUG:",["heatran",20]] ["DEBUG:",["ivysaur",22]] ["DEBUG:",["jellicent",21]] ["DEBUG:",["jumpluff",1]] ["DEBUG:",["kangaskhan",20]] ["DEBUG:",["kricketune",20]] ["DEBUG:",["landorus",21]] ["DEBUG:",["ledyba",2]] ["DEBUG:",["loudred",21]] ["DEBUG:",["lumineon",20]] ["DEBUG:",["lunatone",20]] ["DEBUG:",["machamp",23]] ["DEBUG:",["magnezone",20]] ["DEBUG:",["mamoswine",20]] ["DEBUG:",["nosepass",19]] ["DEBUG:",["petilil",22]] ["DEBUG:",["pidgeotto",1]] ["DEBUG:",["pikachu",1]] ["DEBUG:",["pinsir",22]] ["DEBUG:",["poliwrath",21]] ["DEBUG:",["poochyena",2]] ["DEBUG:",["porygon2",1]] ["DEBUG:",["porygonz",1]] ["DEBUG:",["registeel",21]] ["DEBUG:",["relicanth",21]] ["DEBUG:",["remoraid",21]] ["DEBUG:",["rufflet",21]] ["DEBUG:",["sableye",20]] ["DEBUG:",["scolipede",20]] ["DEBUG:",["scrafty",21]] ["DEBUG:",["seaking",21]] ["DEBUG:",["sealeo",1]] ["DEBUG:",["silcoon",20]] ["DEBUG:",["simisear",21]] ["DEBUG:",["snivy",21]] ["DEBUG:",["snorlax",1]] ["DEBUG:",["spoink",21]] ["DEBUG:",["starly",21]] ["DEBUG:",["tirtouga",2]] ["DEBUG:",["trapinch",20]] ["DEBUG:",["treecko",1]] ["DEBUG:",["tyrogue",20]] ["DEBUG:",["vigoroth",21]] ["DEBUG:",["vulpix",1]] ["DEBUG:",["wailord",21]] ["DEBUG:",["wartortle",20]] ["DEBUG:",["whismur",22]] ["DEBUG:",["wingull",22]] ["DEBUG:",["yamask",20]] [ 23, [ "machamp", "petilil", "landorus", "scrafty", "yamask", "kricketune", "emboar", "registeel", "loudred", "darmanitan", "nosepass", "simisear", "relicanth", "heatmor", "rufflet", "trapinch", "haxorus", "seaking", "girafarig", "gabite", "exeggcute", "emolga", "audino" ] ]
Julia[edit]
Works with: Julia version 0.6
Translation of: Python
using IterTools.groupby orderwords(words::Vector) = Dict(w[1][1] => Set(w) for w in groupby(first, words)) longest(a, b) = ifelse(length(a) > length(b), a, b) function linkfirst(byfirst::Dict, sofar::Vector) @assert(!isempty(sofar)) chmatch = sofar[end][end] if ! haskey(byfirst, chmatch) return sofar end options = setdiff(byfirst[chmatch], sofar) if isempty(options) return sofar else alternatives = ( linkfirst(byfirst, vcat(sofar, word)) for word in options ) mx = reduce(longest, alternatives) return mx end end function llfl(words) byfirst = orderwords(words) alternatives = ( linkfirst(byfirst, [word]) for word in words ) return reduce(longest, alternatives) end pokemon = String.(unique(split(""" audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask """))) l = llfl(pokemon) println("Example of longest seq.:n", join(l, ", ")) println("Max length: ", length(l)
Example of longest seq.: machamp, pinsir, relicanth, heatmor, registeel, landorus, starly, yamask, kricketune, emboar, rufflet, trapinch, haxorus, simisear, remoraid, darmanitan, nosepass, seaking, girafarig, gabite, exeggcute, emolga, audino Max length: 23
Kotlin[edit]
Translation of: Java
// version 1.1.2 var maxPathLength = 0 var maxPathLengthCount = 0 val maxPathExample = StringBuilder(500) val names = arrayOf( "audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask" ) fun search(part: Array<String>, offset: Int) { if (offset > maxPathLength) { maxPathLength = offset maxPathLengthCount = 1 } else if (offset == maxPathLength) { maxPathLengthCount++ maxPathExample.setLength(0) for (i in 0 until offset) { maxPathExample.append(if (i % 5 == 0) "n " else " ") maxPathExample.append(part[i]) } } val lastChar = part[offset - 1].last() for (i in offset until part.size) { if (part[i][0] == lastChar) { val tmp = names[offset] names[offset] = names[i] names[i] = tmp search(names, offset + 1) names[i] = names[offset] names[offset] = tmp } } } fun main(args: Array<String>) { for (i in 0 until names.size) { val tmp = names[0] names[0] = names[i] names[i] = tmp search(names, 1) names[i] = names[0] names[0] = tmp } println("Maximum path length : $maxPathLength") println("Paths of that length : $maxPathLengthCount") println("Example path of that length : $maxPathExample") }
Maximum path length : 23 Paths of that length : 1248 Example path of that length : machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune exeggcute emboar relicanth haxorus simisear registeel landorus seaking girafarig gabite emolga audino
Lua[edit]
In lieu of the poorly-specified extra-credit portion (e.g. are the two Nidoran’s to be considered same or unique?), I chose to expand on the output portion of the core task.
-- BUILDING: pokemon = [[ audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask]] words, inits, succs = {}, {}, {} for word in pokemon:gmatch("%S+") do table.insert(words, word) local ch = word:sub(1,1) inits[ch] = inits[ch] or {} table.insert(inits[ch], word) end for _,word in pairs(words) do succs[word] = {} local ch = word:sub(-1,-1) if inits[ch] then for _,succ in pairs(inits[ch]) do if succ~=word then table.insert(succs[word],succ) end end end end -- SEARCHING: function expand(list, used, answer) local word = list[#list] for _,succ in ipairs(succs[word]) do if not used[succ] then used[succ] = true list[#list+1] = succ if #list == answer.len then local perm = table.concat(list," ") answer.perms[perm] = perm answer.num = answer.num + 1 elseif #list > answer.len then answer.len = #list local perm = table.concat(list," ") answer.perms = {[perm] = perm} answer.num = 1 end expand(list, used, answer) list[#list] = nil used[succ] = nil end end end answers = {} for _,word in ipairs(words) do local answer = { word=word, len=0, num=0, perms={} } answers[#answers+1] = answer expand({word}, {[word]=true}, answer) end -- REPORTING: table.sort(answers, function(a,b) return a.len<b.len or (a.len==b.len and a.word<b.word) end) print("first word length count example") print("---------- ------ ----- -------...") for _,answer in pairs(answers) do local perm = next(answer.perms) or "" print(string.format("%10s %6d %5d %s", answer.word, answer.len, answer.num, perm)) end
first word length count example ---------- ------ ----- -------... audino 0 0 bidoof 0 0 deino 0 0 jumpluff 0 0 pidgeotto 0 0 pikachu 0 0 porygon2 0 0 porygonz 0 0 sealeo 0 0 snorlax 0 0 treecko 0 0 vulpix 0 0 carracosta 2 1 carracosta audino cresselia 2 1 cresselia audino emolga 2 1 emolga audino ledyba 2 1 ledyba audino poochyena 2 1 poochyena audino tirtouga 2 1 tirtouga audino emboar 19 96 emboar relicanth heatmor rufflet trapinch haxorus simisear registeel landorus seaking girafarig gulpin nosepass scrafty yamask kricketune exeggcute emolga audino exeggcute 19 96 exeggcute emboar relicanth haxorus seaking girafarig gulpin nosepass simisear rufflet trapinch heatmor registeel landorus snivy yamask kricketune emolga audino nosepass 19 192 nosepass snivy yamask kricketune exeggcute emboar rufflet trapinch heatmor registeel landorus simisear relicanth haxorus seaking girafarig gabite emolga audino bagon 20 192 bagon nosepass simisear relicanth heatmor rufflet trapinch haxorus scrafty yamask kricketune emboar registeel landorus seaking girafarig gabite exeggcute emolga audino banette 20 192 banette exeggcute emboar relicanth heatmor registeel landorus starly yamask kangaskhan nosepass simisear rufflet trapinch haxorus seaking girafarig gabite emolga audino charmeleon 20 192 charmeleon nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite emboar registeel landorus starly yamask kricketune exeggcute emolga audino darmanitan 20 192 darmanitan nosepass seaking girafarig gabite exeggcute emboar relicanth haxorus simisear rufflet trapinch heatmor registeel landorus starly yamask kricketune emolga audino gabite 20 96 gabite emboar rufflet trapinch heatmor relicanth haxorus simisear registeel landorus seaking girafarig gulpin nosepass snivy yamask kricketune exeggcute emolga audino girafarig 20 480 girafarig gabite emboar registeel landorus spoink kangaskhan nosepass simisear rufflet trapinch heatmor relicanth haxorus snivy yamask kricketune exeggcute emolga audino gulpin 20 192 gulpin nosepass simisear rufflet trapinch heatmor relicanth haxorus snivy yamask kricketune emboar registeel landorus seaking girafarig gabite exeggcute emolga audino haxorus 20 288 haxorus starly yamask kricketune exeggcute emboar registeel landorus simisear rufflet trapinch heatmor remoraid darmanitan nosepass seaking girafarig gabite emolga audino heatmor 20 432 heatmor rufflet trapinch heatran nosepass simisear relicanth haxorus seaking girafarig gabite emboar registeel landorus starly yamask kricketune exeggcute emolga audino heatran 20 192 heatran nosepass simisear relicanth haxorus seaking girafarig gabite exeggcute emboar rufflet trapinch heatmor registeel landorus starly yamask kricketune emolga audino kangaskhan 20 192 kangaskhan nosepass seaking girafarig gabite exeggcute emboar relicanth haxorus simisear rufflet trapinch heatmor registeel landorus starly yamask kricketune emolga audino kricketune 20 96 kricketune exeggcute emboar relicanth heatmor rufflet trapinch haxorus simisear registeel landorus scrafty yamask kangaskhan nosepass seaking girafarig gabite emolga audino lumineon 20 192 lumineon nosepass seaking girafarig gabite exeggcute emboar rufflet trapinch heatmor relicanth haxorus simisear registeel landorus starly yamask kricketune emolga audino lunatone 20 192 lunatone emboar rufflet trapinch heatmor registeel landorus starly yamask kangaskhan nosepass simisear relicanth haxorus seaking girafarig gabite exeggcute emolga audino magnezone 20 192 magnezone exeggcute emboar registeel landorus snivy yamask kangaskhan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite emolga audino mamoswine 20 192 mamoswine exeggcute emboar relicanth haxorus seaking girafarig gulpin nosepass simisear rufflet trapinch heatmor registeel landorus snivy yamask kricketune emolga audino sableye 20 192 sableye emboar relicanth heatmor registeel landorus simisear rufflet trapinch haxorus seaking girafarig gulpin nosepass starly yamask kricketune exeggcute emolga audino scolipede 20 192 scolipede exeggcute emboar relicanth haxorus seaking girafarig gulpin nosepass simisear rufflet trapinch heatmor registeel landorus snivy yamask kricketune emolga audino silcoon 20 192 silcoon nosepass simisear rufflet trapinch haxorus scrafty yamask kricketune exeggcute emboar relicanth heatmor registeel landorus seaking girafarig gabite emolga audino trapinch 20 576 trapinch heatmor registeel landorus simisear remoraid darmanitan nosepass scrafty yamask kricketune exeggcute emboar relicanth haxorus seaking girafarig gabite emolga audino tyrogue 20 192 tyrogue emboar rufflet trapinch heatmor registeel landorus seaking girafarig gulpin nosepass simisear relicanth haxorus snivy yamask kricketune exeggcute emolga audino wartortle 20 192 wartortle exeggcute emboar registeel landorus snivy yamask kangaskhan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite emolga audino yamask 20 96 yamask kangaskhan nosepass seaking girafarig gabite emboar rufflet trapinch heatmor registeel landorus simisear relicanth haxorus spoink kricketune exeggcute emolga audino baltoy 21 96 baltoy yamask kangaskhan nosepass simisear rufflet trapinch heatmor relicanth haxorus seaking girafarig gabite exeggcute emboar registeel landorus spoink kricketune emolga audino braviary 21 96 braviary yamask kangaskhan nosepass seaking girafarig gabite exeggcute emboar relicanth haxorus simisear rufflet trapinch heatmor registeel landorus spoink kricketune emolga audino croagunk 21 288 croagunk kangaskhan nosepass seaking girafarig gabite emboar rufflet trapinch heatmor registeel landorus simisear relicanth haxorus starly yamask kricketune exeggcute emolga audino jellicent 21 768 jellicent trapinch haxorus seaking girafarig gabite exeggcute emboar relicanth heatmor remoraid darmanitan nosepass simisear registeel landorus snivy yamask kricketune emolga audino landorus 21 192 landorus seaking girafarig gabite emboar relicanth heatmor rufflet trapinch haxorus simisear registeel loudred darmanitan nosepass starly yamask kricketune exeggcute emolga audino loudred 21 192 loudred darmanitan nosepass seaking girafarig gabite exeggcute emboar rufflet trapinch haxorus simisear relicanth heatmor registeel landorus snivy yamask kricketune emolga audino poliwrath 21 912 poliwrath heatmor remoraid darmanitan nosepass simisear registeel landorus seaking girafarig gabite exeggcute emboar rufflet trapinch haxorus scrafty yamask kricketune emolga audino registeel 21 192 registeel landorus simisear rufflet trapinch heatmor remoraid darmanitan nosepass seaking girafarig gabite exeggcute emboar relicanth haxorus starly yamask kricketune emolga audino relicanth 21 240 relicanth heatmor rufflet trapinch haxorus simisear registeel landorus seaking girafarig gabite exeggcute emboar remoraid darmanitan nosepass starly yamask kricketune emolga audino remoraid 21 192 remoraid darmanitan nosepass simisear relicanth heatmor registeel landorus scrafty yamask kricketune exeggcute emboar rufflet trapinch haxorus seaking girafarig gabite emolga audino rufflet 21 240 rufflet trapinch heatmor remoraid darmanitan nosepass seaking girafarig gabite emboar registeel landorus simisear relicanth haxorus starly yamask kricketune exeggcute emolga audino scrafty 21 96 scrafty yamask kricketune exeggcute emboar rufflet trapinch heatmor relicanth haxorus spoink kangaskhan nosepass simisear registeel landorus seaking girafarig gabite emolga audino seaking 21 192 seaking girafarig gabite emboar rufflet trapinch heatmor relicanth haxorus simisear registeel landorus snivy yamask kangaskhan nosepass spoink kricketune exeggcute emolga audino simisear 21 384 simisear rufflet trapinch haxorus spoink kricketune exeggcute emboar relicanth heatmor registeel landorus snivy yamask kangaskhan nosepass seaking girafarig gabite emolga audino snivy 21 96 snivy yamask kricketune exeggcute emboar registeel landorus simisear rufflet trapinch heatmor relicanth haxorus spoink kangaskhan nosepass seaking girafarig gabite emolga audino spoink 21 288 spoink kangaskhan nosepass snivy yamask kricketune emboar relicanth heatmor rufflet trapinch haxorus simisear registeel landorus seaking girafarig gabite exeggcute emolga audino starly 21 96 starly yamask kangaskhan nosepass simisear rufflet trapinch heatmor relicanth haxorus seaking girafarig gabite exeggcute emboar registeel landorus spoink kricketune emolga audino vigoroth 21 912 vigoroth haxorus simisear registeel landorus starly yamask kricketune exeggcute emboar rufflet trapinch heatmor remoraid darmanitan nosepass seaking girafarig gabite emolga audino wailord 21 192 wailord darmanitan nosepass seaking girafarig gabite exeggcute emboar rufflet trapinch haxorus simisear relicanth heatmor registeel landorus snivy yamask kricketune emolga audino bronzor 22 864 bronzor registeel landorus seaking girafarig gabite emboar rufflet trapinch heatmor remoraid darmanitan nosepass simisear relicanth haxorus snivy yamask kricketune exeggcute emolga audino ivysaur 22 864 ivysaur registeel landorus seaking girafarig gabite emboar rufflet trapinch heatmor remoraid darmanitan nosepass simisear relicanth haxorus snivy yamask kricketune exeggcute emolga audino petilil 22 384 petilil landorus simisear rufflet trapinch heatmor relicanth haxorus starly yamask kricketune exeggcute emboar registeel loudred darmanitan nosepass seaking girafarig gabite emolga audino pinsir 22 864 pinsir rufflet trapinch heatmor registeel landorus seaking girafarig gabite exeggcute emboar relicanth haxorus simisear remoraid darmanitan nosepass snivy yamask kricketune emolga audino whismur 22 864 whismur registeel landorus seaking girafarig gabite emboar rufflet trapinch heatmor remoraid darmanitan nosepass simisear relicanth haxorus snivy yamask kricketune exeggcute emolga audino wingull 22 384 wingull landorus snivy yamask kricketune emboar rufflet trapinch haxorus simisear relicanth heatmor registeel loudred darmanitan nosepass seaking girafarig gabite exeggcute emolga audino machamp 23 1248 machamp petilil landorus simisear relicanth haxorus starly yamask kricketune exeggcute emboar rufflet trapinch heatmor registeel loudred darmanitan nosepass seaking girafarig gabite emolga audino
Mathematica/Wolfram Language[edit]
longestChain[list_] := NestWhileList[ Append @@@ Select[DeleteDuplicatesBy[ Tuples[{#, list}], {#[[1, 1]], #[[2]]} &], ! MemberQ @@ # && StringTake[#[[1, -1]], -1] == StringTake[#[[2]], 1] &] &, List /@ list, # != {} &][[-2, 1]]; Print[longestChain[{"audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask"}]];
Uses the tactic of only checking chains with the same starting and ending values.
{baltoy, yamask, kangaskhan, nosepass, sableye, emboar, registeel, landorus, scolipede, emolga, audino}
Nim[edit]
Using bit sets of indexes. The program runs in 0.5s.
import tables const Names = ["audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask"] type Index = range[0..Names.len-1] IndexSet = set[Index] Successors = Table[Index, IndexSet] const All = {Index.low..Index.high} # All indexes. func initSuccessors(): Successors {.compileTime.} = ## Build the mapping from name indexes to set of possible successor indexes. var names: Table[char, IndexSet] # Map first char to IndexSet. for idx, name in Names: names.mgetOrPut(name[0], {}).incl(idx) for idx, name in Names: result[idx] = names.getOrDefault(name[^1]) - {idx} # Mapping name index -> set of successor indexes. const Succ = initSuccessors() proc search(starts, available: IndexSet): seq[Index] = ## Search one of the longest sequence of indexes for given ## starting indexes and given available name indexes. var maxLen = -1 for idx in starts * available: let list = search(Succ[idx], available - {idx}) if list.len > maxLen: result = idx & list maxLen = list.len let list = search(starts = All, available = All) echo "Longest lists have length: ", list.len echo "One of these lists is:" for idx in list: echo Names[idx]
Longest lists have length: 23 One of these lists is: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
ooRexx[edit]
-- create the searcher and run it searcher = .chainsearcher~new ::class chainsearcher ::method init expose max searchsize currentlongest pokemon_names = "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon" - "cresselia croagunk darmanitan deino emboar emolga exeggcute gabite" - "girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan" - "kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine" - "nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2" - "porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking" - "sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko" - "tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask" pokemon = pokemon_names~makearray(" ") searchsize = pokemon~items currentlongest = 0 say "searching" searchsize "names..." longestchain = .array~new -- run the sequence using each possible starting pokemon loop i = 1 to pokemon~items -- swap the ith name to the front of our list self~swap(pokemon, 1, i) -- run the chain from here self~searchChain(pokemon, longestchain, 2) -- swap the name back so we have the list in original form self~swap(pokemon, 1, i) end say "maximum path length:" longestchain~items say "paths of that length:" max say "example path of that length:" loop name over longestchain say " "name end ::method swap use arg list, a, b tmp = list[a] list[a] = list[b] list[b] = tmp -- recursive search routine for adding to the chain ::method searchChain expose max searchsize currentlongest use arg pokemon, longestchain, currentchain -- get the last character lastchar = pokemon[currentchain - 1]~right(1) -- now we search through all of the permutations of remaining -- matches to see if we can find a longer chain loop i = currentchain to searchsize -- for every candidate name from here, recursively extend the chain. if pokemon[i]~left(1) == lastchar then do if currentchain == currentLongest then max += 1 -- have we now gone deeper than the current longest chain? else if currentchain > currentLongest then do -- chuck this result and refill with current set longestchain~empty longestchain~appendall(pokemon~section(1, currentchain - 1)) longestchain~append(pokemon[i]) max = 1 currentLongest = currentchain end -- perform the swap again self~swap(pokemon, currentchain, i) -- run the chain from here self~searchChain(pokemon, longestchain, currentchain + 1) -- swap the name back so we have the list in original form self~swap(pokemon, currentchain, i) end end
searching 70 names... maximum path length: 23 paths of that length: 1248 example path of that length: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
OpenEdge/Progress[edit]
The following gets the job done, but the time taken (40 minutes) is somewhat worrying when compared to other language solutions. So I am not going after the brownie points just yet…
DEFINE VARIABLE cpokemon AS CHARACTER INITIAL "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon ~ cresselia croagunk darmanitan deino emboar emolga exeggcute gabite ~ girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan ~ kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine ~ nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 ~ porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking ~ sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko ~ tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask". DEFINE TEMP-TABLE tt NO-UNDO FIELD cname AS CHARACTER FIELD cfirst AS CHARACTER FIELD clast AS CHARACTER FIELD lused AS LOGICAL FIELD ilength AS INTEGER FIELD imax AS INTEGER FIELD cchain AS CHARACTER INDEX ttname cname INDEX ttfirst cfirst lused INDEX ttlast clast lused . DEFINE VARIABLE ii AS INTEGER NO-UNDO. DO ii = 1 TO NUM-ENTRIES( cpokemon, " " ): CREATE tt. ASSIGN tt.cname = ENTRY( ii, cpokemon, " " ) tt.cfirst = SUBSTRING( tt.cname, 1, 1 ) tt.clast = SUBSTRING( tt.cname, LENGTH( tt.cname ), 1 ) . END. FUNCTION getChain RETURNS INTEGER ( i_cname AS CHARACTER, i_clast AS CHARACTER, i_ilength AS INTEGER, i_cchain AS CHARACTER ): DEFINE BUFFER tt FOR tt. DEFINE VARIABLE lend_of_chain AS LOGICAL NO-UNDO INITIAL TRUE. FOR EACH tt WHERE tt.cfirst = i_clast AND tt.lused = FALSE OR i_clast = "" : lend_of_chain = FALSE. tt.lused = TRUE. getChain( tt.cname, tt.clast, i_ilength + 1, i_cchain + tt.cname + " " ). tt.lused = FALSE. END. IF lend_of_chain THEN DO: FIND tt WHERE tt.cname = ENTRY( 1, i_cchain, " " ). IF i_ilength = tt.ilength THEN tt.imax = tt.imax + 1. ELSE IF i_ilength > tt.ilength THEN ASSIGN tt.ilength = i_ilength tt.cchain = i_cchain tt.imax = 1 . END. END FUNCTION. /* getChain */ DEFINE VARIABLE itime AS INTEGER NO-UNDO EXTENT 2. DEFINE VARIABLE lcontinue AS LOGICAL NO-UNDO. itime[1] = ETIME. getChain( "", "", 0, "" ). itime[2] = ETIME. FOR EACH tt BY tt.ilength DESCENDING: MESSAGE "Maximum path length:" tt.ilength SKIP "Paths of that length:" tt.imax SKIP(1) "Example path of that length:" tt.cchain SKIP(1) "Time taken:" STRING( INTEGER( ( itime[2] - itime[1] ) / 1000 ), "HH:MM:SS" ) VIEW-AS ALERT-BOX BUTTONS YES-NO TITLE tt.cname UPDATE lcontinue. IF lcontinue = FALSE THEN STOP. END.
Output:
--------------------------- machamp --------------------------- Maximum path length: 23 Paths of that length: 1248 Example path of that length: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino Time taken: 00:40:09 --------------------------- Yes No ---------------------------
Perl[edit]
This is rather ‘one liner’ code, not to be used in production.
The idea is to try all possible variants recursively.
- First, it creates the map-like structure: first letter → array of (name + last letter).
- During the cycle it uses @w as stack;
- @m keeps the longest sequence which is copied from @w;
- to prevent the words from appearing twice, they are (temporarily) deleted from the structure keeping the value in a stack variable.
use strict; my(%f,@m); /^(.).*(.)$/,$f{$1}{$_}=$2 for qw( audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask ); sub poke { our @w; my $h = $f{$_[0]}; for my $word (keys %$h) { my $v = $h->{$word}; delete $h->{$word}; push @w, $word; @m = @w if @w > @m; poke($v); pop @w; $h->{$word} = $v; } } poke($_) for keys %f; print @m.": @mn";
23: machamp petilil landorus seaking girafarig gabite emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus scrafty yamask kricketune exeggcute emolga audino
Phix[edit]
Using simple start-with-same-letter word chains to minimise the number of elements we have to consider:
with javascript_semantics constant words = {"audino","bagon","baltoy","banette","bidoof","braviary","bronzor","carracosta","charmeleon","cresselia","croagunk", "darmanitan","deino","emboar","emolga","exeggcute","gabite","girafarig","gulpin","haxorus","heatmor","heatran", "ivysaur","jellicent","jumpluff","kangaskhan","kricketune","landorus","ledyba","loudred","lumineon","lunatone", "machamp","magnezone","mamoswine","nosepass","petilil","pidgeotto","pikachu","pinsir","poliwrath","poochyena", "porygon2","porygonz","registeel","relicanth","remoraid","rufflet","sableye","scolipede","scrafty","seaking", "sealeo","silcoon","simisear","snivy","snorlax","spoink","starly","tirtouga","trapinch","treecko","tyrogue", "vigoroth","vulpix","wailord","wartortle","whismur","wingull","yamask"} function word_chains() sequence first = repeat(0,256), -- start of chains for a given letter -- first['a']=1, first['b']=2, first['c']=8, etc. snext = repeat(0,length(words)) -- chains of words starting with the same letter -- a: snext[1]=0, b: snext[2..7]={3,4,5,6,7,0}, etc. for i=1 to length(words) do integer ch = words[i][1] if first[ch]=0 then first[ch] = i end if for j=i+1 to length(words) do if words[j][1]=ch then snext[i] = j exit end if end for end for return {first,snext} end function constant {first,snext} = word_chains() -- maintain words already taken as a linked list: integer tstart sequence taken = repeat(0,length(words)) -- 0=no, -1=end of chain, +ve=next -- and keep a copy of the best for later integer bstart sequence best integer maxn = 0, count procedure find_path(integer ch, last, n) integer next = first[ch] while next!=0 do if taken[next]=0 then taken[last] = next taken[next] = -1 find_path(words[next][$],next,n+1) taken[last] = -1 taken[next] = 0 end if next = snext[next] end while if n>maxn then bstart = tstart best = deep_copy(taken) maxn = n count = 1 elsif n=maxn then count += 1 end if end procedure atom t0=time() for i=1 to length(words) do tstart = i taken[i] = -1 find_path(words[i][$],i,1) taken[i] = 0 end for printf(1,"Runtime: %2.3f seconds. Max length:%d, found %d of such, one of which is:n",{time()-t0,maxn,count}) sequence res = {} while bstart!=-1 do res = append(res,words[bstart]) bstart = best[bstart] end while assert(length(res)=maxn) printf(1,"%sn",join_by(res,1,10," "))
Runtime: 0.625 seconds. Max length:23, found 1248 of such, one of which is: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
My claim for the extra brownie points, using the list from the Racket entry:
Runtime: 0.000 seconds. Max length:2, found 9 of such, one of which is: Porygon-Z Zubat
Which is quite correct: 9 names begin with ‘Z’, and plenty with ‘T’ but none with ‘t’
Adding a couple of upper(), it found a 302 in a couple of minutes but was still on permutations starting
with word[1] when I got bored and killed it, quickly calculating at the very least another 4.5 days and
quite probably not finishing within my lifetime…
Picat[edit]
It’s a little faster with the words sorted on decreasing lengths (words2
): 9.43s vs 11.742s.
go => words(Words), % Words that starts with same letter as <this words>'s last letter. Starts = new_map(), foreach(Word in Words) S = [ Word2 : Word2 in Words, Word != Word2, Word.last() = Word2.first()], Starts.put(Word,S) end, % Sort the words according to lengths in decreasing order. Words2 = [W : W=_ in Starts.map_to_list().qsort(sort_len)], % Start the search MaxLen = _, Continue := true, foreach(Len in 2..Words.len, break(Continue == false)) if play1(Words2,Starts,Len,_List) then MaxLen := Len else Continue := false end end, println(maxLen=MaxLen), % And present the result. println("nGet some (5) solutions:"), get_some_solutions(Words2, Starts, MaxLen,5), println("nNnumber of optimal solutions:"), NumSols = count_all(play1(Words2,Starts,MaxLen,_List)), println(num_sols=NumSols), nl. % Check if it's possible to create a list of length Len. play1(Words, Starts, Len, LLFL) :- LLFL1 = new_list(Len), select(LLFL1[1], Words, Rest), C = 2, while (C <= Len) Among = Starts.get(LLFL1[C-1]), Among != [], select(Word,Among,Rest2), not membchk(Word,LLFL1[1..C-1]), LLFL1[C] := Word, Rest := Rest2, C := C + 1 end, LLFL = LLFL1. % Print NumSols solutions get_some_solutions(Words,Starts,FoundLen,NumSols) => Map = get_global_map(), Map.put(count,1), play1(Words, Starts, FoundLen, LLFL), println(LLFL), C = Map.get(count), if C < NumSols then Map.put(count,C+1), fail end. % qsort(List, SortFunction) % returns a sorted list according to the sort function SortFunction. qsort([],_F) = []. qsort([H|T],F) = qsort([E : E in T, call(F,E,H)], F) ++ [H] ++ qsort([E : E in T, not call(F,E,H)],F). % Sort according to length sort_len((_K1=V1),(_K2=V2)) :- V1.len >= V2.len. words(Words) => Words = [ "audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask" ].
maxLen = 23 Get some (5) solutions: [machamp,petilil,landorus,scrafty,yamask,kricketune,emboar,registeel,loudred,darmanitan,nosepass,simisear,relicanth,heatmor,rufflet,trapinch,haxorus,seaking,girafarig,gabite,exeggcute,emolga,audino] [machamp,petilil,landorus,scrafty,yamask,kricketune,emboar,registeel,loudred,darmanitan,nosepass,simisear,rufflet,trapinch,heatmor,relicanth,haxorus,seaking,girafarig,gabite,exeggcute,emolga,audino] [machamp,petilil,landorus,scrafty,yamask,kricketune,emboar,relicanth,haxorus,simisear,rufflet,trapinch,heatmor,registeel,loudred,darmanitan,nosepass,seaking,girafarig,gabite,exeggcute,emolga,audino] [machamp,petilil,landorus,scrafty,yamask,kricketune,emboar,relicanth,heatmor,registeel,loudred,darmanitan,nosepass,simisear,rufflet,trapinch,haxorus,seaking,girafarig,gabite,exeggcute,emolga,audino] [machamp,petilil,landorus,scrafty,yamask,kricketune,emboar,relicanth,heatmor,rufflet,trapinch,haxorus,simisear,registeel,loudred,darmanitan,nosepass,seaking,girafarig,gabite,exeggcute,emolga,audino] Nnumber of optimal solutions: num_sols = 1248
PicoLisp[edit]
(de pokemonChain (File) (let Names (make (in File (while (read) (link @)))) (for Name Names (let C (last (chop Name)) (set Name (filter '((Nm) (pre? C Nm)) Names) ) ) ) (let Res NIL (for Name Names (let Lst NIL (recur (Name Lst) (if (or (memq Name Lst) (not (val (push 'Lst Name)))) (when (> (length Lst) (length Res)) (setq Res Lst) ) (mapc recurse (val Name) (circ Lst)) ) ) ) ) (flip Res) ) ) )
Test:
: (pokemonChain "pokemon.list") -> (machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino) : (length @) -> 23
Prolog[edit]
Works with SWI-Prolog and module lambda.pl written by Ulrich Neumerkel found there http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/lambda.pl
:- use_module(library(lambda)). :- dynamic res/3. last_first(Len, Nb, L) :- retractall(res(_,_,_)), assert(res(0, 0, [])), % compute all the lists of connected words last_first, res(Len, Nb, L1), % to have only the words maplist(X^Y^(X = [Y, _, _]), L1, L). % create the lists of connected words (initiate the first word) last_first :- init(L), forall(select(Word, L, L1), +lance_p([Word | L1])). % compute all the lists beginning with a word % memorize the longest lance_p(L) :- p(LF, L), retract(res(Len, Nb, Lst)), length(LF, Len1), ( Len1 > Len -> assert(res(Len1, 1, LF)) ; Len1 = Len -> Nb1 is Nb + 1, assert(res(Len, Nb1, Lst)) ; assert(res(Len, Nb, Lst))), fail. % describe the property of the list of connected words p([A | T], [A | L]) :- select(B, L, L1), p0(A,B), T = [B | T1], p([B | T1], [B | L1]). % a list with one element is valid p([_], _). % are words conected ? p0([_, _, W], [_, W, _]). % each word is associated with its first and last letters % audino --> [audino, a, o] init(L) :- L0 = [ audino, bagon, baltoy, banette, bidoof, braviary, bronzor, carracosta, charmeleon, cresselia, croagunk, darmanitan, deino, emboar, emolga, exeggcute, gabite, girafarig, gulpin, haxorus, heatmor, heatran, ivysaur, jellicent, jumpluff, kangaskhan, kricketune, landorus, ledyba, loudred, lumineon, lunatone, machamp, magnezone, mamoswine, nosepass, petilil, pidgeotto, pikachu, pinsir, poliwrath, poochyena, porygon2, porygonz, registeel, relicanth, remoraid, rufflet, sableye, scolipede, scrafty, seaking, sealeo, silcoon, simisear, snivy, snorlax, spoink, starly, tirtouga, trapinch, treecko, tyrogue, vigoroth, vulpix, wailord, wartortle, whismur, wingull, yamask], maplist(init_, L0, L). % audino --> [audino, a, o] init_(W, [W, F, L]) :- first_letter(W, F), last_letter(W, L). first_letter(A, F) :- atom_chars(A, [F | _]). last_letter(A, L) :- atom_chars(A, LC), reverse(LC, [L | _]).
Output :
?- time(last_first(Len, Nb, L)). % 592,161,339 inferences, 125.690 CPU in 128.264 seconds (98% CPU, 4711284 Lips) Len = 23, Nb = 1248, L = [machamp,petilil,landorus,scrafty,yamask,kricketune,emboar,registeel,loudred,darmanitan,nosepass,simisear,relicanth,heatmor,rufflet,trapinch,haxorus,seaking,girafarig,gabite,exeggcute,emolga,audino].
Python[edit]
from collections import defaultdict def order_words(words): byfirst = defaultdict(set) for word in words: byfirst[word[0]].add( word ) #byfirst = dict(byfirst) return byfirst def linkfirst(byfirst, sofar): ''' For all words matching last char of last word in sofar as FIRST char and not in sofar, return longest chain as sofar + chain ''' assert sofar chmatch = sofar[-1][-1] options = byfirst[chmatch] - set(sofar) #print(' linkfirst options: %r %r' % (chmatch, options)) if not options: return sofar else: alternatives = ( linkfirst(byfirst, list(sofar) + [word]) for word in options ) mx = max( alternatives, key=len ) #input('linkfirst: %r' % mx) return mx def llfl(words): byfirst = order_words(words) return max( (linkfirst(byfirst, [word]) for word in words), key=len ) if __name__ == '__main__': pokemon = '''audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask''' pokemon = pokemon.strip().lower().split() pokemon = sorted(set(pokemon)) l = llfl(pokemon) for i in range(0, len(l), 8): print(' '.join(l[i:i+8])) print(len(l))
- Sample output
machamp pinsir relicanth heatmor remoraid darmanitan nosepass snivy yamask kricketune exeggcute emboar registeel landorus simisear rufflet trapinch haxorus seaking girafarig gabite emolga audino 23
Alternative version[edit]
Adapted from the D version. This uses Psyco.
import psyco nsolutions = 0 def search(sequences, ord_minc, curr_word, current_path, current_path_len, longest_path): global nsolutions current_path[current_path_len] = curr_word current_path_len += 1 if current_path_len == len(longest_path): nsolutions += 1 elif current_path_len > len(longest_path): nsolutions = 1 longest_path[:] = current_path[:current_path_len] # recursive search last_char_index = ord(curr_word[-1]) - ord_minc if last_char_index >= 0 and last_char_index < len(sequences): for pair in sequences[last_char_index]: if not pair[1]: pair[1] = True search(sequences, ord_minc, pair[0], current_path, current_path_len, longest_path) pair[1] = False def find_longest_chain(words): ord_minc = ord(min(word[0] for word in words)) ord_maxc = ord(max(word[0] for word in words)) sequences = [[] for _ in xrange(ord_maxc - ord_minc + 1)] for word in words: sequences[ord(word[0]) - ord_minc].append([word, False]) current_path = [None] * len(words) longest_path = [] # try each item as possible start for seq in sequences: for pair in seq: pair[1] = True search(sequences, ord_minc, pair[0], current_path, 0, longest_path) pair[1] = False return longest_path def main(): global nsolutions pokemon = """audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask""".lower().split() # remove duplicates pokemon = sorted(set(pokemon)) sol = find_longest_chain(pokemon) print "Maximum path length:", len(sol) print "Paths of that length:", nsolutions print "Example path of that length:" for i in xrange(0, len(sol), 7): print " ", " ".join(sol[i : i+7]) psyco.full() main()
Output:
Maximum path length: 23 Paths of that length: 1248 Example path of that length: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
Run time: about 0.44 seconds with Psyco and Python 2.6.6.
Racket[edit]
This is a naive solution, which works fast enough as is (takes about 5 seconds on an old machine):
#lang racket (define names " audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask") (struct word (first last string) #:prefab) (define words (for/list ([str (string-split names)]) (word (string->symbol (substring str 0 1)) (string->symbol (substring str (sub1 (string-length str)))) str))) (define (find-longest last words) (for/fold ([best '()]) ([w (in-list words)] #:when (or (not last) (eq? last (word-first w)))) (define long (cons w (find-longest (word-last w) (remq w words)))) (if (> (length long) (length best)) long best))) (define longest (find-longest #f words)) (printf "Longest chain found has ~a words:n ~an" (length longest) (string-join (map word-string longest) " -> "))
Longest chain found has 23 words: machamp -> petilil -> landorus -> scrafty -> yamask -> kricketune -> emboar -> registeel -> loudred -> darmanitan -> nosepass -> simisear -> relicanth -> heatmor -> rufflet -> trapinch -> haxorus -> seaking -> girafarig -> gabite -> exeggcute -> emolga -> audino
This is a (probably over-)complex version which tries to construct longest
chains using three different combination/relinking functions. Not that the
definiton of `word` is slightly different here.
#lang racket (require "pokemon-names.rkt") ;;; Some fundamentals... finding the first (henceforth referred to as "a") and last ("z") ;;; letters of a word can be computationally intensive... look at symbol->word, and you'll ;;; see that when we have to deal with a name like: "Mime Jr.", the last alphabetic letter ;;; is not the last character. And the first and last characters (at least) have to be ;;; case-normalised so they can be compared with char=? (it's not particulary helpful to ;;; map them down to integer character codes; we'll want to see them for debugging). (define-struct word (sym char-a char-z) #:prefab) ;;; names are input as symbols both for ease of comparsion, and because it's easier to type (define (symbol->word sym) (let* ((str (symbol->string sym)) (chrs (string->list str)) (fst (for/first ((c chrs) #:when (char-alphabetic? c)) (char-downcase c))) (lst (for/last ((c chrs) #:when (char-alphabetic? c)) (char-downcase c)))) (make-word sym fst lst))) ;;; We're sometimes not interested in debugging a chain of; just in knowing how long it is ;;; and what its extreme characters are. This does the trick. (define (summarise-chain c) (format "(~a.~a.~a)" (word-char-a (first c)) (sub1 (length c)) (word-char-z (last c)))) ;; Test the above (run `raco test last_letter-first_letter-common.rkt`) (define-syntax-rule (hash-set-or-remove hsh key val remove-pred?) (let ((v val)) (if (remove-pred? v) (hash-remove hsh key) (hash-set hsh key v)))) (define-syntax-rule (find-a-in-chain-pool chains a dont-match-sym) (for/first ((c chains) (i (in-naturals)) ;; usually need an index for regenerating chains pool ;; a word can only exist in one chain, so this compares chains' identities #:unless (eq? (word-sym (first c)) dont-match-sym) #:when (char=? (word-char-a (first c)) a)) (cons i c))) (define-syntax-rule (copy-list-ignoring-indices lst i1 i2) (for/list ((idx (in-naturals)) (e (in-list lst)) #:unless (= idx i1) #:unless (= idx i2)) e)) ;; Simple ... find a chain that can be put on the end of c... append it, and ;; reiterate (define (append-ab..bz-chains chain chain-idx chains) (let* ((a1.chain-a (find-a-in-chain-pool chains (word-char-z (last chain)) (word-sym (first chain))))) (and a1.chain-a (let ((a1.chain-idx (first a1.chain-a)) (a1.chain-chain (rest a1.chain-a))) (cons (append chain a1.chain-chain) (copy-list-ignoring-indices chains chain-idx a1.chain-idx)))))) ;; If chain has an a..a loop in it, then we see if we can take that loop, and ;; place it in a longer chain at a point where a is used. ;; ;; `chain` is the shorter chain containing the loop (define (merge-chain-into-chain-accepting-a..a-in-chain chain chain-idx chains) ;; for a..a loops in chain, returns a hash from the looped char, to the longest ;; found loop (define (find-a..a-loops chain) (let ((chain-length (length chain))) (for*/fold ((hsh (hash))) ((sub-range-start (in-range chain-length)) (aa (in-value (word-char-a (list-ref chain sub-range-start)))) (sub-range-end (in-range sub-range-start chain-length)) #:when (eq? aa (word-char-z (list-ref chain sub-range-end))) (range-length (in-value (add1 (- sub-range-end sub-range-start))))) (hash-update hsh aa (lambda (longest-range) (if (and longest-range (> (third longest-range) range-length)) longest-range (list sub-range-start sub-range-end range-length))) #f)))) (let* ((chain-first-name (word-sym (first chain))) (chain-length (length chain)) (a..a-list (sort (hash->list (find-a..a-loops chain)) > #:key third))) (for*/first (((chain2 chain2-idx) (in-parallel (in-list chains) (in-naturals))) #:unless (eq? chain-first-name (word-sym (car chain2))) (chain2-length (in-value (length chain2))) #:when (>= chain2-length chain-length) ; only move the largest a..a-subchain into a larger chain (a..a (in-list a..a-list)) ((insertion-point-word insertion-point-idx) (in-parallel (in-list chain2) (in-naturals))) #:when (eq? (car a..a) (word-char-a insertion-point-word))) (let* ((new-chain (append (take chain (second a..a)) (drop chain (add1 (third a..a))))) (a..a-chain (take (drop chain (second a..a)) (fourth a..a))) (new-chain2 (append (take chain2 insertion-point-idx) a..a-chain (drop chain2 insertion-point-idx)))) (let ((new-chains (copy-list-ignoring-indices chains chain-idx chain2-idx))) (if (null? new-chain) (cons new-chain2 new-chains) (cons new-chain (cons new-chain2 new-chains)))))))) ;; this is a bit more combinatorially intensive... for all c2, substitute a ;; subrange in c2 that is longer than an equivalent subrange in c (define (merge-subranges-of-chains-into-chain chain chain-idx chains) (let ((chain-first-name (word-sym (first chain))) (chain-length (length chain)) (chain-first-a (word-char-a (first chain))) (chain-last-z (word-char-z (last chain)))) (for*/first ; try to replace a subrange in c2 with c (((chain2 chain2-idx) (in-parallel (in-list chains) (in-naturals))) (chain2-length (in-value (length chain2))) #:unless (eq? chain-first-name (word-sym (car chain2))) (c2-sub-range-start (in-range chain2-length)) (c2-sub-range-end (in-range c2-sub-range-start (min chain2-length (+ c2-sub-range-start chain-length)))) #:unless (and (= c2-sub-range-start 0) (= c2-sub-range-end (sub1 chain2-length))) #:when (or (zero? c2-sub-range-start) (eq? (word-char-a (list-ref chain2 c2-sub-range-start)) chain-first-a)) #:when (or (= (sub1 chain2-length) c2-sub-range-end) (eq? (word-char-z (list-ref chain2 c2-sub-range-end)) chain-last-z)) (c2-sub-range-len (in-value (add1 (- c2-sub-range-end c2-sub-range-start)))) #:when (> chain-length c2-sub-range-len) (c2-sub-range (in-value (take (drop chain2 c2-sub-range-start) c2-sub-range-len))) (new-c2 (in-value (append (take chain2 c2-sub-range-start) chain (drop chain2 (add1 c2-sub-range-end)))))) (cons c2-sub-range ; put the subrange back into the chains pool (cons new-c2 ; put the modified onto the chains pool (copy-list-ignoring-indices chains chain-idx chain2-idx)))))) (define (longest-chain/constructive names #:known-max (known-max +inf.0)) (define names-list (map symbol->word names)) (define (link-chains chains) (let ((new-chains (for*/first (((chain chain-idx) (in-parallel (in-list chains) (in-naturals))) (new-chains (in-value (or (append-ab..bz-chains chain chain-idx chains) (merge-chain-into-chain-accepting-a..a-in-chain chain chain-idx chains) (merge-subranges-of-chains-into-chain chain chain-idx chains)))) #:when new-chains) new-chains))) (if new-chains (link-chains new-chains) chains))) (define (keep-trying (run-count 0) (linked-chains (link-chains (map list (shuffle names-list)))) (previous-best null) (previous-best-length 0) (this-run-best-length #f)) (let* ((longest-chain (argmax length linked-chains)) (longest-chain-len (length longest-chain)) (new-best? (< previous-best-length longest-chain-len)) (best-length (if new-best? longest-chain-len previous-best-length)) (best (if new-best? longest-chain previous-best))) (when new-best? (newline) (displayln (list (map word-sym longest-chain) longest-chain-len)) (flush-output)) (if (and new-best? (>= best-length known-max)) (displayln "terminating: known max reached or exceeded") (begin (when (zero? (modulo (add1 run-count) 250)) (eprintf ".") (flush-output (current-error-port))) (if (= run-count 1000) (keep-trying 0 (link-chains (map list (shuffle names-list))) best best-length 0) (let ((sorted-linked-chains (sort linked-chains #:key length >))) (keep-trying (if new-best? 0 (add1 run-count)) (link-chains (cons (car sorted-linked-chains) (map list (shuffle (apply append (cdr sorted-linked-chains)))))) best best-length (and this-run-best-length (if new-best? #f (if (< this-run-best-length longest-chain-len) (begin (eprintf ">~a" longest-chain-len) (flush-output (current-error-port)) longest-chain-len) this-run-best-length)))))))))) (keep-trying)) (time (longest-chain/constructive names-70 #:known-max 23)) (longest-chain/constructive names-646)
Run with racket -t last_letter-first_letter-randomised.rkt 2>&1, to redirect standard error to
standard out.
((machamp petilil landorus seaking girafarig gabite exeggcute emboar relicanth heatran nosepass simisear registeel loudred deino) 15) ((machamp petilil landorus seaking girafarig gabite exeggcute emboar relicanth haxorus snivy yamask kangaskhan nosepass simisear registeel lunatone emolga audino) 19) ((machamp petilil landorus seaking girafarig gabite exeggcute emboar rufflet trapinch heatmor relicanth haxorus snivy yamask kangaskhan nosepass simisear registeel lunatone emolga audino) 22) ....>10>17>19....>14>17>20>21....>15>18>21>22....>16>17>22....>15>17>18>21....>19>20....>16>18>20>22....>14>21 ((machamp petilil landorus seaking girafarig gabite emboar rufflet trapinch heatmor relicanth haxorus simisear registeel loudred darmanitan nosepass starly yamask kricketune exeggcute emolga audino) 23) terminating: known max reached or exceeded cpu time: 24077 real time: 24070 gc time: 36
As an aside… I’ve had this take 40 s; and I’ve had it run in 35 ms. It really is the luck of the draw!
((Jellicent Tangrowth Hippopotas Slowking Gyarados Spiritomb Breloom Mandibuzz Zweilous Shellos Stoutland Deoxys Swoobat Togekiss Seedot Toxicroak Krokorok Kabutops Seadra Anorith Heracross Slakoth Haxorus Shelmet Tauros Skiploom Manectric Castform Milotic Chingling Golem Mothim Meganium Metapod Ducklett Tornadus Swellow Whismur Rufflet Taillow Wormadam Medicham Mismagius Snubbull Latias Solosis Sandshrew Woobat Teddiursa Abomasnow Wingull Lapras Seel Lillipup Paras Sandslash Hoppip Prinplup Piplup Petilil Lickilicky Yanmega Abra Ariados Solrock Klink Kingler Relicanth Hariyama Altaria Azumarill Luvdisc Charmander Rotom Maractus Skuntank Kirlia Azelf Farfetch’d Delibird Dratini Illumise Exploud Diglett Torkoal Lampent Turtwig Girafarig Golbat Tepig Grumpig Granbull Larvesta Azurill Loudred Dialga Amoonguss Sigilyph Herdier Rampardos Seaking Golett Timburr Raichu Umbreon Noctowl Latios Sawk Kyurem Miltank Klinklang Gorebyss Slaking Gliscor Rhyhorn Nidorina Alakazam Machamp Pidgeot Trubbish Hitmontop Politoed Dewgong Gardevoir Regigigas Swampert Tympole Electrode Exeggcute Eelektrik Kingdra Accelgor Remoraid Dewott Tentacool Litwick Koffing Gigalith Hypno Octillery Yanma Arcanine Elekid Dusknoir Reshiram Mesprit Tropius Swalot Tranquill Luxray Yamask Kyogre Eevee Electivire Emolga Alomomola Ampharos Spinarak Kricketune Exeggutor Roselia Arbok Krookodile Escavalier Rayquaza Aron Ninetales Serperior Registeel Lileep Patrat Throh Hitmonlee Espeon Nidoqueen Ninjask Kakuna Axew Wigglytuff Frillish Houndour Regice Eelektross Swinub Budew Whiscash Hoothoot Torterra Aipom Mareep Poliwag Grimer Roggenrola Armaldo Omastar Rhyperior Ralts Scolipede Excadrill Luxio Oshawott Togetic Crawdaunt Torchic Cofagrigus Shroomish Houndoom Marill Larvitar Raticate Electrike Ekans Starmie Emboar Regirock Karrablast Tangela Aerodactyl Lairon Nidoran Natu Unfezant Trapinch Horsea Archen Nidoran Nosepass Staravia Ambipom Murkrow Weezing Gothita Absol Lotad Darmanitan Nincada Arceus Spheal Lilligant Tynamo Omanyte Elgyem Mew Watchog Golduck Kabuto Oddish Ho-Oh Heatran Nidoking Golurk Klang Garchomp Porygon-Z Zekrom Meowth Hippowdon Nuzleaf Feraligatr Rapidash Haunter Reuniclus Seviper Raikou Ursaring Glalie Entei Igglybuff Froslass Spearow Wailord Duskull Ludicolo Onix Xatu Unown Numel Landorus Seismitoad Deerling Grovyle Empoleon Nidorino) 283)
…
((Voltorb Beartic Croconaw Wynaut Tangrowth Hippopotas Smoochum Moltres Shuppet Thundurus Slowking Gyarados Spiritomb Breloom Mandibuzz Zweilous Shellos Stoutland Deoxys Swoobat Togekiss Seedot Toxicroak Krokorok Kabutops Snorunt Tirtouga Anorith Heracross Slakoth Haxorus Shelmet Tauros Skiploom Manectric Castform Milotic Chingling Golem Mothim Meganium Metapod Ducklett Tornadus Swellow Whismur Rufflet Taillow Wormadam Medicham Mismagius Snubbull Latias Solosis Sandshrew Woobat Teddiursa Abomasnow Wingull Lapras Seel Lillipup Paras Sandslash Hoppip Prinplup Piplup Probopass Stunfisk Krabby Yanmega Abra Ariados Solrock Klink Kingler Relicanth Hariyama Altaria Azumarill Luvdisc Chatot Tyranitar Rotom Maractus Skuntank Kirlia Azelf Farfetch'd Delibird Dratini Ivysaur Riolu Uxie Electabuzz Zapdos Sawsbuck Kyogre Exploud Diglett Torkoal Lampent Turtwig Girafarig Golbat Tepig Grumpig Granbull Larvesta Azurill Loudred Dialga Amoonguss Sigilyph Herdier Rampardos Seaking Golett Timburr Raichu Umbreon Noctowl Latios Sawk Kyurem Miltank Klinklang Gorebyss Slaking Gigalith Huntail Ledyba Aggron Nidorina Alakazam Machamp Pidgeot Trubbish Hitmontop Politoed Dewgong Gardevoir Regigigas Swampert Tentacruel Lickitung Glameow Whirlipede Electrode Exeggcute Eelektrik Kingdra Accelgor Remoraid Dewott Tentacool Litwick Koffing Gloom Marshtomp Psyduck Kadabra Articuno Octillery Yanma Arcanine Elekid Dusknoir Reshiram Mesprit Tropius Swalot Tranquill Luxray Yamask Kricketot Tyrogue Eevee Electivire Emolga Alomomola Ampharos Sentret Togepi Infernape Exeggutor Roselia Arbok Krookodile Escavalier Rayquaza Aron Ninetales Serperior Registeel Lileep Patrat Throh Honchkrow Wobbuffet Totodile Espeon Nidoqueen Ninjask Kakuna Axew Wigglytuff Frillish Houndour Regice Eelektross Swinub Budew Whiscash Hoothoot Torterra Aipom Mareep Poliwag Grimer Roggenrola Armaldo Omastar Rhyperior Ralts Surskit Tympole Excadrill Lugia Audino Oshawott Togetic Crawdaunt Torchic Cofagrigus Shroomish Houndoom Mudkip Ponyta Archeops Spinarak Kricketune Electrike Ekans Simisear Raticate Emboar Regirock Karrablast Tangela Aerodactyl Liepard Dusclops Spoink Kecleon Nidoran Natu Unfezant Trapinch Horsea Archen Nidoran Nosepass Snover Rattata Ambipom Murkrow Weezing Gothita Absol Lotad Durant Terrakion Nincada Arceus Spheal Lilligant Tynamo Omanyte Elgyem Mew Watchog Golduck Kabuto Oddish Ho-Oh Heatran Nidoking Golurk Klang Garchomp Porygon-Z Zekrom Magikarp Pawniard Deerling Gurdurr Rhydon Nuzleaf Foongus Shellder Rapidash Haunter Reuniclus Seviper Raikou Ursaring Garbodor Roserade Entei Igglybuff Froslass Spearow Wailord Duskull Ludicolo Onix Xatu Unown Numel Landorus Seismitoad Drifblim Machop Palpitoad Darkrai Illumise Empoleon Nidorino) 329)
Never terminates. The longest chain I have found (so far?) is 333 long:
((Voltorb Breloom Medicham Machop Piplup Palpitoad Deerling Golett Turtwig Glameow Wingull Lilligant Timburr Registeel Lotad Duskull Latias Serperior Reuniclus Spinarak Krokorok Klinklang Golem Mew Whiscash Huntail Lugia Accelgor Rattata Aerodactyl Luvdisc Chingling Grumpig Garchomp Prinplup Petilil Loudred Ducklett Tornadus Spearow Whimsicott Tangrowth Hoothoot Tentacruel Lampent Togekiss Skiploom Marshtomp Patrat Thundurus Solosis Sneasel Lillipup Poliwhirl Lapras Sandslash Honchkrow Wobbuffet Torkoal Latios Slaking Gligar Rampardos Shellos Sentret Tropius Sigilyph Herdier Remoraid Dusclops Stoutland Drilbur Reshiram Misdreavus Slowking Golduck Klink Koffing Gyarados Shuppet Tauros Swellow Wynaut Torchic Castform Maractus Spiritomb Bayleef Furret Tangela Altaria Aipom Murkrow Wigglytuff Forretress Spheal Lickitung Gigalith Heracross Snorunt Totodile Exeggcute Electrode Ekans Solrock Klang Girafarig Gorebyss Swalot Trapinch Ho-Oh Hitmontop Porygon-Z Zapdos Shroomish Houndour Relicanth Hoppip Poliwrath Houndoom Mareep Poliwag Golurk Kirlia Axew Wormadam Mandibuzz Zweilous Swinub Beldum Manectric Cherrim Moltres Staraptor Rayquaza Azurill Lairon Nidoqueen Noctowl Leavanny Yamask Kricketune Eevee Escavalier Rufflet Torterra Archen Ninjask Kakuna Absol Lunatone Excadrill Ludicolo Octillery Yanma Archeops Spoink Kabuto Onix Xatu Unown Nidoran Ninetales Sawsbuck Kyurem Magikarp Politoed Dewott Trubbish Hippopotas Seel Linoone Empoleon Nincada Amoonguss Swoobat Teddiursa Audino Oshawott Tepig Gloom Metagross Shelmet Tentacool Larvesta Ariados Skorupi Illumise Exploud Durant Tynamo Omastar Roggenrola Arbok Kricketot Taillow Weezing Grimer Rhyperior Rapidash Haxorus Skuntank Kingdra Alomomola Azelf Ferroseed Drifblim Marowak Kingler Roselia Azumarill Lanturn Natu Ursaring Golbat Toxicroak Kecleon Numel Landorus Samurott Togepi Infernape Electrike Electivire Electabuzz Zekrom Metang Galvantula Arceus Seedot Tirtouga Aron Nosepass Seismitoad Dialga Articuno Oddish Horsea Alakazam Meganium Marill Liepard Deoxys Smoochum Mudkip Probopass Stunfisk Krookodile Elekid Delibird Dratini Igglybuff Foongus Swampert Tyranitar Raikou Uxie Eelektross Slakoth Hariyama Abra Anorith Haunter Raichu Umbreon Nidorina Aggron Nuzleaf Floatzel Litwick Kyogre Exeggutor Raticate Emboar Regirock Kabutops Sandshrew Watchog Gurdurr Roserade Entei Ivysaur Regice Emolga Abomasnow Wailord Diglett Tyrogue Espeon Nidoran Nidoking Granbull Ledyba Arcanine Elgyem Mothim Mismagius Sawk Kadabra Ampharos Snubbull Larvitar Riolu Unfezant Togetic Charizard Dewgong Gothita Ambipom Machamp Paras Simisear Ralts Seaking Gliscor Rotom Milotic Cyndaquil Lileep Pawniard Dragonair Regigigas Surskit Tranquill Ledian Nidorino Omanyte Eelektrik Karrablast Throh Happiny Yanmega Armaldo) 333)
File: pokemon-names.rkt
#lang racket (provide names-646 names-70) (define names-70 '(audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask)) ; If this were true, then the 2 would be ignored, which seems a bit wrong; so wordigy the "2" -> "two" (define Porygon2 (if #f 'Porygon2 'Porygon-two)) ; note that Porygon2 is used with a comma in the list below (define names-646 `(Bulbasaur Ivysaur Venusaur Charmander Charmeleon Charizard Squirtle Wartortle Blastoise Caterpie Metapod Butterfree Weedle Kakuna Beedrill Pidgey Pidgeotto Pidgeot Rattata Raticate Spearow Fearow Ekans Arbok Pikachu Raichu Sandshrew Sandslash Nidoran Nidorina Nidoqueen Nidoran Nidorino Nidoking Clefairy Clefable Vulpix Ninetales Jigglypuff Wigglytuff Zubat Golbat Oddish Gloom Vileplume Paras Parasect Venonat Venomoth Diglett Dugtrio Meowth Persian Psyduck Golduck Mankey Primeape Growlithe Arcanine Poliwag Poliwhirl Poliwrath Abra Kadabra Alakazam Machop Machoke Machamp Bellsprout Weepinbell Victreebel Tentacool Tentacruel Geodude Graveler Golem Ponyta Rapidash Slowpoke Slowbro Magnemite Magneton |Farfetch'd| Doduo Dodrio Seel Dewgong Grimer Muk Shellder Cloyster Gastly Haunter Gengar Onix Drowzee Hypno Krabby Kingler Voltorb Electrode Exeggcute Exeggutor Cubone Marowak Hitmonlee Hitmonchan Lickitung Koffing Weezing Rhyhorn Rhydon Chansey Tangela Kangaskhan Horsea Seadra Goldeen Seaking Staryu Starmie |Mr. Mime| Scyther Jynx Electabuzz Magmar Pinsir Tauros Magikarp Gyarados Lapras Ditto Eevee Vaporeon Jolteon Flareon Porygon Omanyte Omastar Kabuto Kabutops Aerodactyl Snorlax Articuno Zapdos Moltres Dratini Dragonair Dragonite Mewtwo Mew Chikorita Bayleef Meganium Cyndaquil Quilava Typhlosion Totodile Croconaw Feraligatr Sentret Furret Hoothoot Noctowl Ledyba Ledian Spinarak Ariados Crobat Chinchou Lanturn Pichu Cleffa Igglybuff Togepi Togetic Natu Xatu Mareep Flaaffy Ampharos Bellossom Marill Azumarill Sudowoodo Politoed Hoppip Skiploom Jumpluff Aipom Sunkern Sunflora Yanma Wooper Quagsire Espeon Umbreon Murkrow Slowking Misdreavus Unown Wobbuffet Girafarig Pineco Forretress Dunsparce Gligar Steelix Snubbull Granbull Qwilfish Scizor Shuckle Heracross Sneasel Teddiursa Ursaring Slugma Magcargo Swinub Piloswine Corsola Remoraid Octillery Delibird Mantine Skarmory Houndour Houndoom Kingdra Phanpy Donphan ,Porygon2 Stantler Smeargle Tyrogue Hitmontop Smoochum Elekid Magby Miltank Blissey Raikou Entei Suicune Larvitar Pupitar Tyranitar Lugia Ho-Oh Celebi Treecko Grovyle Sceptile Torchic Combusken Blaziken Mudkip Marshtomp Swampert Poochyena Mightyena Zigzagoon Linoone Wurmple Silcoon Beautifly Cascoon Dustox Lotad Lombre Ludicolo Seedot Nuzleaf Shiftry Taillow Swellow Wingull Pelipper Ralts Kirlia Gardevoir Surskit Masquerain Shroomish Breloom Slakoth Vigoroth Slaking Nincada Ninjask Shedinja Whismur Loudred Exploud Makuhita Hariyama Azurill Nosepass Skitty Delcatty Sableye Mawile Aron Lairon Aggron Meditite Medicham Electrike Manectric Plusle Minun Volbeat Illumise Roselia Gulpin Swalot Carvanha Sharpedo Wailmer Wailord Numel Camerupt Torkoal Spoink Grumpig Spinda Trapinch Vibrava Flygon Cacnea Cacturne Swablu Altaria Zangoose Seviper Lunatone Solrock Barboach Whiscash Corphish Crawdaunt Baltoy Claydol Lileep Cradily Anorith Armaldo Feebas Milotic Castform Kecleon Shuppet Banette Duskull Dusclops Tropius Chimecho Absol Wynaut Snorunt Glalie Spheal Sealeo Walrein Clamperl Huntail Gorebyss Relicanth Luvdisc Bagon Shelgon Salamence Beldum Metang Metagross Regirock Regice Registeel Latias Latios Kyogre Groudon Rayquaza Jirachi Deoxys Turtwig Grotle Torterra Chimchar Monferno Infernape Piplup Prinplup Empoleon Starly Staravia Staraptor Bidoof Bibarel Kricketot Kricketune Shinx Luxio Luxray Budew Roserade Cranidos Rampardos Shieldon Bastiodon Burmy Wormadam Mothim Combee Vespiquen Pachirisu Buizel Floatzel Cherubi Cherrim Shellos Gastrodon Ambipom Drifloon Drifblim Buneary Lopunny Mismagius Honchkrow Glameow Purugly Chingling Stunky Skuntank Bronzor Bronzong Bonsly |Mime Jr.| Happiny Chatot Spiritomb Gible Gabite Garchomp Munchlax Riolu Lucario Hippopotas Hippowdon Skorupi Drapion Croagunk Toxicroak Carnivine Finneon Lumineon Mantyke Snover Abomasnow Weavile Magnezone Lickilicky Rhyperior Tangrowth Electivire Magmortar Togekiss Yanmega Leafeon Glaceon Gliscor Mamoswine Porygon-Z Gallade Probopass Dusknoir Froslass Rotom Uxie Mesprit Azelf Dialga Palkia Heatran Regigigas Giratina Cresselia Phione Manaphy Darkrai Shaymin Arceus Victini Snivy Servine Serperior Tepig Pignite Emboar Oshawott Dewott Samurott Patrat Watchog Lillipup Herdier Stoutland Purrloin Liepard Pansage Simisage Pansear Simisear Panpour Simipour Munna Musharna Pidove Tranquill Unfezant Blitzle Zebstrika Roggenrola Boldore Gigalith Woobat Swoobat Drilbur Excadrill Audino Timburr Gurdurr Conkeldurr Tympole Palpitoad Seismitoad Throh Sawk Sewaddle Swadloon Leavanny Venipede Whirlipede Scolipede Cottonee Whimsicott Petilil Lilligant Basculin Sandile Krokorok Krookodile Darumaka Darmanitan Maractus Dwebble Crustle Scraggy Scrafty Sigilyph Yamask Cofagrigus Tirtouga Carracosta Archen Archeops Trubbish Garbodor Zorua Zoroark Minccino Cinccino Gothita Gothorita Gothitelle Solosis Duosion Reuniclus Ducklett Swanna Vanillite Vanillish Vanilluxe Deerling Sawsbuck Emolga Karrablast Escavalier Foongus Amoonguss Frillish Jellicent Alomomola Joltik Galvantula Ferroseed Ferrothorn Klink Klang Klinklang Tynamo Eelektrik Eelektross Elgyem Beheeyem Litwick Lampent Chandelure Axew Fraxure Haxorus Cubchoo Beartic Cryogonal Shelmet Accelgor Stunfisk Mienfoo Mienshao Druddigon Golett Golurk Pawniard Bisharp Bouffalant Rufflet Braviary Vullaby Mandibuzz Heatmor Durant Deino Zweilous Hydreigon Larvesta Volcarona Cobalion Terrakion Virizion Tornadus Thundurus Reshiram Zekrom Landorus Kyurem))
Raku[edit]
(formerly Perl 6)
A breadth-first search that uses disk files to avoid memory exhaustion. Each candidate sequence is encoded at one character per name, so to avoid reuse of names we merely have to make sure there are no repeat characters in our encoded string. (The encoding starts at ASCII space for the first name, so newline is not among the encoded characters.)
my @names = < audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask >; my @last = @names.map: {.substr(*-1,1).ord } my @succs = [] xx 128; for @names.kv -> $i, $name { my $ix = $name.ord; # $name.substr(0,1).ord push @succs[$ix], $i; } my $OUT = open "llfl.new", :w orelse .die; $OUT.print: chr($_ + 32),"n" for 0 ..^ @names; close $OUT; my $new = +@names; my $len = 1; while $new { say "Length { $len++ }: $new candidates"; shell 'mv llfl.new llfl.known'; my $IN = open "llfl.known" orelse .die; my $OUT = open "llfl.new", :w orelse .die; $new = 0; loop { my $cand = $IN.get // last; for @succs[@last[$cand.ord - 32]][] -> $i { my $ic = chr($i + 32); next if $cand ~~ /$ic/; $OUT.print: $ic,$cand,"n"; $new++; } } $IN.close; $OUT.close; } my $IN = open "llfl.known" orelse .die; my $eg = $IN.lines.pick; say "Length of longest: ", $eg.chars; say join ' ', $eg.ords.reverse.map: { @names[$_ - 32] }
Length 1: 70 candidates Length 2: 172 candidates Length 3: 494 candidates Length 4: 1288 candidates Length 5: 3235 candidates Length 6: 7731 candidates Length 7: 17628 candidates Length 8: 37629 candidates Length 9: 75122 candidates Length 10: 139091 candidates Length 11: 236679 candidates Length 12: 367405 candidates Length 13: 516210 candidates Length 14: 650916 candidates Length 15: 733915 candidates Length 16: 727566 candidates Length 17: 621835 candidates Length 18: 446666 candidates Length 19: 260862 candidates Length 20: 119908 candidates Length 21: 40296 candidates Length 22: 10112 candidates Length 23: 1248 candidates Length of longest: 23 machamp petilil loudred darmanitan nosepass simisear rufflet trapinch heatmor registeel landorus starly yamask kricketune exeggcute emboar relicanth haxorus seaking girafarig gabite emolga audino
REXX[edit]
Translation of: ooRexx
brute force version[edit]
(This program is modeled after the ooRexx version, but with a fixed bug.)
This REXX version allows a limit on the word scan (very useful for testing and debugging), and
also has various speed optimizations.
/*REXX program finds the longest path of word's last─letter ───► first─letter. */ @='audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan', 'deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent', 'jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine', 'nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth', 'remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink', 'starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask' #= words(@); ig= 0; !.= 0; @.= /*nullify array and the longest path. */ parse arg limit .; if limit=='' then #=limit /*allow user to specify a scan limit. */ call build@ /*build a stemmed array from the @ list*/ do v=# by -1 for # /*scrub the @ list for unusable words. */ parse var @.v F 2 '' -1 L /*obtain first and last letter of word.*/ if !.1.F>1 | !.9.L>1 then iterate /*is this a dead word?*/ say 'ignoring dead word:' @.v; ig= ig + 1; @= delword(@, v, 1) end /*v*/ /*delete dead word from @ ──┘ */ $$$= /*nullify the possible longest path. */ if ig==0 then do; call build@; say; say 'ignoring' ig "dead word"s(ig).; say end MP= 0; MPL= 0 /*the initial Maximum Path Length. */ do j=1 for # /* ─ ─ ─ */ parse value @.1 @.j with @.j @.1; call scan $$$, 2 parse value @.1 @.j with @.j @.1 end /*j*/ g= words($$$) say 'Of' # "words," MP 'path's(MP) "have the maximum path length of" g'.' say; say 'One example path of that length is: ' word($$$, 1) do m=2 to g; say left('', 36) word($$$, m) end /**/ exit 0 /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ s: if arg(1)==1 then return arg(3); return word( arg(2) 's', 1) /*a pluralizer.*/ /*──────────────────────────────────────────────────────────────────────────────────────*/ build@: do i=1 for #; @.i=word(@, i) /*build a stemmed array from the list. */ F= left(@.i, 1); !.1.F= !.1.F + 1 /*F: 1st char; !.1.F=count of 1st char*/ L=right(@.i, 1); !.9.L= !.9.L + 1 /*L: last " !.9.L= " " last " */ end /*i*/; return /*──────────────────────────────────────────────────────────────────────────────────────*/ scan: procedure expose @. # !. $$$ MP MPL; parse arg $$$,!; p=! - 1 parse var @.p '' -1 LC /*obtain last character of previous @. */ if !.1.LC==0 then return /*is this a dead─end word? */ /* [↓] PARSE obtains first char of @.i*/ do i=! to #; parse var @.i p 2 /*scan for the longest word path. */ if p==LC then do /*is the first─character ≡ last─char? */ if !==MPL then MP= MP+1 /*bump the Maximum Paths Counter. */ else if !>MPL then do; $$$=@.1 /*rebuild. */ do n=2 for !-2; $$$=$$$ @.n end /*n*/ $$$=$$$ @.i /*add last.*/ MP=1; MPL=! /*new path.*/ end parse value @.! @.i with @.i @.!; call scan $$$, !+1 parse value @.! @.i with @.i @.! end end /*i*/; return /*exhausted this particular scan. */
- output when using the default input:
Of 70 words, 1248 paths have the maximum path length of 23. One example path of that length is: machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
optimized version[edit]
This optimized version has two major improvements:
-
-
- removes dead words (words that cannot be used in a path)
- stops scanning when a dead-end word is encountered.
-
With the full list of words being used, there are no dead words (but there are when a limit is used to shorten the list).
In the scan subroutine, a check is made to see if the word being used is a dead-end word, and if so, the rest of
the recursive scan is aborted and the the next word is scanned.
The optimized version is around 25% faster than the brute-force version.
/*REXX program finds the longest path of word's last─letter ───► first─letter. */ @='audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan', 'deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent', 'jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine', 'nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth', 'remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink', 'starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask' #= words(@); @.=; @s=@.; ig=0; og=0; !.=0 /*nullify array and the longest path. */ parse arg limit .; if limit=='' then #= limit /*allow user to specify a scan limit. */ call build@ /*build a stemmed array from the @ list*/ do v=# by -1 for # /*scrub the @ list for unusable words. */ parse var @.v F 2 '' -1 L /*obtain first and last letter of word.*/ if !.1.F>1 | !.9.L>1 then iterate /*is this a dead word? */ say 'ignoring dead word:' @.v; ig= ig + 1; @= delword(@, v, 1) end /*v*/ /*delete dead word from @ ──┘ */ #= words(@) /*recalculate the number of words in @.*/ do v=# by -1 for # /*scrub the @ list for stoppable words.*/ parse var @.v F 2 '' -1 L /*obtain first and last letter of word.*/ if !.1.L>0 then iterate /*is this a stop word? */ if @.v=='' then say 'removing stop word:' @.v og= og + 1; @= delword(@, v, 1); @s= @s @.v end /*v*/ /*delete dead word from @ ──┘ */ if og==0 then do; call build@; say; say 'ignoring' og "stop word"s(og). say 'stop words: ' @s; say end $$$= /*nullify the possible longest path. */ MP= 0; MPL= 0 /*the initial Maximum Path Length. */ do j=1 for # /* ─ ─ ─ */ parse value @.1 @.j with @.j @.1; call scan $$$, 2 parse value @.1 @.j with @.j @.1 end /*j*/ g= words($$$) say 'Of' # "words," MP 'path's(MP) "have the maximum path length of" g'.' say; say 'One example path of that length is: ' word($$$, 1) do m=2 to g; say left('', 36) word($$$, m) end /*m*/ exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ s: if arg(1)==1 then return arg(3); return word( arg(2) 's', 1) /*a pluralizer.*/ /*──────────────────────────────────────────────────────────────────────────────────────*/ build@: do i=1 for #; @.i= word(@, i) /*build a stemmed array from the list. */ F= left(@.i, 1); !.1.F= !.1.F + 1 /*F: 1st char; !.1.F=count of 1st char*/ L= right(@.i, 1); !.9.L= !.9.L + 1 /*L: last " !.9.L= " " last " */ end /*i*/; return /*──────────────────────────────────────────────────────────────────────────────────────*/ scan: procedure expose @. # !. $$$ MP MPL; parse arg $$$,!; p= ! - 1 parse var @.p '' -1 LC /*obtain last character of previous @. */ if !.1.LC==0 then return /*is this a dead─end word? */ /* [↓] PARSE obtains first char of @.i*/ do i=! to #; parse var @.i p 2 /*scan for the longest word path. */ if p==LC then do /*is the first─character ≡ last─char? */ if !==MPL then MP= MP+1 /*bump the Maximum Paths Counter. */ else if !>MPL then do; $$$= @.1 /*rebuild. */ do n=2 for !-2; $$$=$$$ @.n end /*n*/ $$$= $$$ @.i /*add last.*/ MP=1; MPL=! /*new path.*/ end parse value @.! @.i with @.i @.!; call scan $$$, !+1 parse value @.! @.i with @.i @.! end end /*i*/; return /*exhausted this particular scan. */
- output is the same as the brute force version.
Ring[edit]
# Project : Last letter-first letter ready = [] names = ["audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask"] strbegin = "gabite" strdir = "first" nr = 1 add(ready,strbegin) see strbegin + nl while true if strdir = "first" strc = right(strbegin, 1) flag = 1 nr = nr + 1 if nr <= len(names) if left(names[nr],1) = strc for n = 1 to len(ready) if names[nr] = ready[n] flag = 0 ok next if flag = 1 add(ready,names[nr]) see names[nr] + nl strbegin = names[nr] nr = 0 strdir = "last" loop ok ok ok else strc = right(strbegin, 1) flag = 1 nr = nr + 1 if nr <= len(names) if left(names[nr],1) = strc for n = 1 to len(ready) if names[nr] = ready[n] flag = 0 ok next if flag = 1 add(ready,names[nr]) see names[nr] + nl strbegin = names[nr] nr = 0 strdir = "first" loop ok ok ok ok end
Output:
gabite emboar registeel landorus sableye emolga audino
Ruby[edit]
class LastL_FirstL def initialize(names) @names = names.dup @first = names.group_by {|name| name[0]} @sequences = [] end def add_name(seq) last_letter = seq[-1][-1] potentials = @first.include?(last_letter) ? (@first[last_letter] - seq) : [] if potentials.empty? @sequences << seq else potentials.each {|name| add_name(seq + [name])} end end def search @names.each {|name| add_name [name]} max = @sequences.max_by {|seq| seq.length}.length max_seqs = @sequences.select {|seq| seq.length == max} puts "there are #{@sequences.length} possible sequences" puts "the longest is #{max} names long" puts "there are #{max_seqs.length} such sequences. one is:" max_seqs.last.each_with_index {|name, idx| puts " %2d %s" % [idx+1, name]} end end names = %w{ audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask } lf = LastL_FirstL.new(names) lf.search
outputs
there are 2076396 possible sequences the longest is 23 names long there are 1248 such sequences. one is: 1 machamp 2 pinsir 3 rufflet 4 trapinch 5 heatmor 6 remoraid 7 darmanitan 8 nosepass 9 starly 10 yamask 11 kricketune 12 exeggcute 13 emboar 14 relicanth 15 haxorus 16 simisear 17 registeel 18 landorus 19 seaking 20 girafarig 21 gabite 22 emolga 23 audino
Rust[edit]
Translation of: Kotlin
/// # Panics /// /// If string is empty. fn first_char(string: &str) -> char { string.chars().next().unwrap() } /// # Panics /// /// If string is empty. fn first_and_last_char(string: &str) -> (char, char) { ( first_char(string), first_char(string.rmatches(|_: char| true).next().unwrap()), ) } struct Pokemon { name: &'static str, first: char, last: char, } impl Pokemon { fn new(name: &'static str) -> Pokemon { let (first, last) = first_and_last_char(name); Pokemon { name, first, last } } } #[derive(Default)] struct App { max_path_length: usize, max_path_length_count: usize, max_path_example: Vec<&'static str>, pokemon: Vec<Pokemon>, } impl App { fn search(&mut self, offset: usize) { if offset > self.max_path_length { self.max_path_length = offset; self.max_path_length_count = 1; } else if offset == self.max_path_length { self.max_path_length_count += 1; self.max_path_example.clear(); self.max_path_example.extend( self.pokemon[0..offset] .iter() .map(|Pokemon { name, .. }| *name), ); } let last_char = self.pokemon[offset - 1].last; for i in offset..self.pokemon.len() { if self.pokemon[i].first == last_char { self.pokemon.swap(offset, i); self.search(offset + 1); self.pokemon.swap(offset, i); } } } } fn main() { let pokemon_names = [ "audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask", ]; let mut app = App { pokemon: pokemon_names .iter() .map(|name| Pokemon::new(name)) .collect(), ..App::default() }; for i in 0..app.pokemon.len() { app.pokemon.swap(0, i); app.search(1); app.pokemon.swap(0, i); } println!("Maximum path length: {}", app.max_path_length); println!("Paths of that length: {}", app.max_path_length_count); println!( "Example path of that length: {}", app.max_path_example.join(" "), ); }
Recommend you run as `release` or at least with `opt-level=1`
Maximum path length: 23 Paths of that length: 1248 Example path of that length: machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune exeggcute emboar relicanth haxorus simisear registeel landorus seaking girafarig gabite emolga audino
Scala[edit]
Naive[edit]
object LastLetterFirstLetterNaive extends App { def solve(names: Set[String]) = { def extend(solutions: List[List[String]]): List[List[String]] = { val more = solutions.flatMap{solution => val lastLetter = solution.head takeRight 1 (names -- solution).filter(_.take(1) equalsIgnoreCase lastLetter).map(_ :: solution) } if (more.isEmpty) solutions else extend(more) } extend(names.toList.map(List(_))).map(_.reverse) } val names70 = Set("audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask") val solutions = solve(names70) println(s"Maximum path length: ${solutions.head.length}") println(s"Paths of that length: ${solutions.size}") println("Example path of that length:") println(solutions.head.sliding(7,7).map(_.mkString(" ")).map(" "+_).mkString("n")) }
Output:
Maximum path length: 23 Paths of that length: 1248 Example path of that length: machamp petilil loudred darmanitan nosepass snivy yamask kricketune emboar rufflet trapinch heatmor relicanth haxorus simisear registeel landorus seaking girafarig gabite exeggcute emolga audino
With Lookup Table (Faster)[edit]
object LastLetterFirstLetterLookup extends App { def solve(names: Set[String]) = { val transitions = { val startsWith = names.groupBy(_.take(1).toLowerCase).withDefaultValue(Set.empty) names.map{name => name -> startsWith(name.takeRight(1).toLowerCase)}.toMap } def extend(solutions: List[List[String]]): List[List[String]] = solutions.flatMap{solution => (transitions(solution.head) -- solution).map(_ :: solution) } match { case Nil => solutions case more => extend(more) } extend(names.toList.map(List(_))).map(_.reverse) } val names70 = Set("audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask") val solutions = solve(names70) println(s"Maximum path length: ${solutions.head.length}") println(s"Paths of that length: ${solutions.size}") println("Example path of that length:") println(solutions.head.sliding(7,7).map(_.mkString(" ")).map(" "+_).mkString("n")) }
Parallelised With Lookup Table (Faster)[edit]
object LastLetterFirstLetterLookupParallel extends App { def solve(names: Set[String]) = { val transitions = { val startsWith = names.groupBy(_.take(1).toLowerCase).withDefaultValue(Set.empty) names.map{name => name -> startsWith(name.takeRight(1).toLowerCase)}.toMap } import scala.collection.parallel.immutable.ParSeq def extend(solutions: ParSeq[List[String]]): ParSeq[List[String]] = solutions.flatMap{solution => (transitions(solution.head) -- solution).map(_ :: solution) } match { case seq if seq.isEmpty => solutions case more => extend(more) } extend(names.toList.map(List(_)).par).map(_.reverse) } val names70 = Set("audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask") val solutions = solve(names70) println(s"Maximum path length: ${solutions.head.length}") println(s"Paths of that length: ${solutions.size}") println("Example path of that length:") println(solutions.head.sliding(7,7).map(_.mkString(" ")).map(" "+_).mkString("n")) }
Seed7[edit]
$ include "seed7_05.s7i"; var integer: maxPathLength is 0; var integer: maxPathLengthCount is 0; var string: maxPathExample is ""; var array string: names is [] ( "audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask"); const proc: recursive (in array string: part, in integer: offset) is func local var integer: index is 0; var char: lastChar is ' '; var string: tmp is ""; begin if pred(offset) > maxPathLength then maxPathLength := pred(offset); maxPathLengthCount := 1; elsif pred(offset) = maxPathLength then incr(maxPathLengthCount); maxPathExample := ""; for index range 1 to pred(offset) do if pred(index) rem 5 = 0 then maxPathExample &:= "n "; else maxPathExample &:= " "; end if; maxPathExample &:= part[index]; end for; end if; lastChar := part[pred(offset)][length(part[pred(offset)])]; for index range offset to length(part) do if part[index][1] = lastChar then tmp := names[offset]; names[offset] := names[index]; names[index] := tmp; recursive(names, succ(offset)); names[index] := names[offset]; names[offset] := tmp; end if; end for; end func; const proc: main is func local var integer: index is 0; var string: tmp is ""; begin for index range 1 to length(names) do tmp := names[1]; names[1] := names[index]; names[index] := tmp; recursive(names, 2); names[index] := names[1]; names[1] := tmp; end for; writeln("maximum path length: " <& maxPathLength lpad 4); writeln("paths of that length: " <& maxPathLengthCount lpad 4); writeln("example path of that length:" <& maxPathExample); end func;
Output:
maximum path length: 23 paths of that length: 1248 example path of that length: machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune exeggcute emboar relicanth haxorus simisear registeel landorus seaking girafarig gabite emolga audino
Sidef[edit]
Translation of: Perl
class LLFL(Array words) { has f = Hash() method init { words.each {|w| var m = w.match(/^(.).*(.)$/) f{m[0]}{w} = m[1] } } method longest_chain { var stack = [] var longest = [] func poke(c) { var h = f{c} h.each_k { |word| var v = h.delete(word) stack.push(word) longest[] = stack[] if (stack.len > longest.len) __FUNC__(v) if f.exists(v) stack.pop h{word} = v } } f.each_k { poke(_) } return longest } } var words = %w( audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask ) var obj = LLFL(words) var longest = obj.longest_chain() say "#{longest.len}: #{longest.join(' ')}"
23: machamp petilil landorus scrafty yamask kricketune exeggcute emboar registeel loudred darmanitan nosepass simisear rufflet trapinch heatmor relicanth haxorus seaking girafarig gabite emolga audino
Tcl[edit]
proc search {path arcs} { set solutions {} set c [string index [lindex $path end] end] set i -1 foreach arc $arcs { incr i if {[string index $arc 0] ne $c} continue set soln [search [concat $path [list $arc]] [lreplace $arcs $i $i]] lappend solutions [list [llength $soln] $soln] } if {[llength $solutions]} { return [lindex [lsort -integer -decreasing -index 0 $solutions] 0 1] } else { return $path } } proc firstlast names { set solutions {} set i -1 foreach initial $names { incr i set soln [search [list $initial] [lreplace $names $i $i]] lappend solutions [list [llength $soln] $soln] } return [lindex [lsort -integer -decreasing -index 0 $solutions] 0 1] } set names { audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask } set path [firstlast $names] puts "Path (length: [llength $path]): $path"
Output:
Path (length 23): machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
Ursala[edit]
#import std mon = ~&*~ sep` mat` -[ audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon cresselia croagunk darmanitan deino emboar emolga exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask]- poke = @iiDrzhK16rlXOASK24PiX ~&llrHiFPrYX^=rxS^|~&iNCS *=+ ~&rlwNrlCQ^*D/~&+ @h #show+ example = ~&h poke mon
output:
machamp petilil landorus scrafty yamask kricketune emboar registeel loudred darmanitan nosepass simisear relicanth heatmor rufflet trapinch haxorus seaking girafarig gabite exeggcute emolga audino
VBScript[edit]
Translation of: BBC BASIC
' Last letter-first letter - VBScript - 11/03/2019 names = array( _ "audino", "bagon", "baltoy", "banette", _ "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", _ "cresselia", "croagunk", "darmanitan", "deino", "emboar", _ "emolga", "exeggcute", "gabite", "girafarig", "gulpin", _ "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", _ "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", _ "loudred", "lumineon", "lunatone", "machamp", "magnezone", _ "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", _ "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", _ "registeel", "relicanth", "remoraid", "rufflet", "sableye", _ "scolipede", "scrafty", "seaking", "sealeo", "silcoon", _ "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", _ "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", _ "wailord", "wartortle", "whismur", "wingull", "yamask") maxPathLength = 0 maxPathLengthCount = 0 maxPathExample = "" t1=timer For i = 0 To ubound(names) 'swap names(0), names(i) temp=names(0): names(0)=names(i): names(i)=temp Call lastfirst(names, 1) 'swap names(0), names(i) temp=names(0): names(0)=names(i): names(i)=temp Next 'i buf = buf & "Maximum length = " & maxPathLength & vbCrLf buf = buf & "Number of solutions with that length = " & maxPathLengthCount & vbCrLf buf = buf & "One such solution: " & vbCrLf & maxPathExample & vbCrLf t2=timer MsgBox buf,,"Last letter-first letter - " & Int(t2-t1) & " sec" Sub lastfirst(names, offset) dim i, l If offset > maxPathLength Then maxPathLength = offset maxPathLengthCount = 1 ElseIf offset = maxPathLength Then maxPathLengthCount = maxPathLengthCount + 1 maxPathExample = "" For i = 0 To offset-1 maxPathExample = maxPathExample & names(i) & vbCrLf Next 'i End If l = Right(names(offset - 1),1) For i = offset To ubound(names) If Left(names(i),1) = l Then 'swap names(i), names(offset) temp=names(offset): names(offset)=names(i): names(i)=temp Call lastfirst(names, offset+1) 'swap names(i), names(offset) temp=names(offset): names(offset)=names(i): names(i)=temp End If Next 'i End Sub 'lastfirst
Maximum length = 23 Number of solutions with that length = 1248 One such solution: machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune exeggcute emboar relicanth haxorus simisear registeel landorus seaking girafarig gabite emolga audino
Duration : 69 sec
Wren[edit]
Translation of: Kotlin
var maxPathLength = 0 var maxPathLengthCount = 0 var maxPathExample = "" var names = [ "audino", "bagon", "baltoy", "banette", "bidoof", "braviary", "bronzor", "carracosta", "charmeleon", "cresselia", "croagunk", "darmanitan", "deino", "emboar", "emolga", "exeggcute", "gabite", "girafarig", "gulpin", "haxorus", "heatmor", "heatran", "ivysaur", "jellicent", "jumpluff", "kangaskhan", "kricketune", "landorus", "ledyba", "loudred", "lumineon", "lunatone", "machamp", "magnezone", "mamoswine", "nosepass", "petilil", "pidgeotto", "pikachu", "pinsir", "poliwrath", "poochyena", "porygon2", "porygonz", "registeel", "relicanth", "remoraid", "rufflet", "sableye", "scolipede", "scrafty", "seaking", "sealeo", "silcoon", "simisear", "snivy", "snorlax", "spoink", "starly", "tirtouga", "trapinch", "treecko", "tyrogue", "vigoroth", "vulpix", "wailord", "wartortle", "whismur", "wingull", "yamask" ] var search // recursive function search = Fn.new { |part, offset| if (offset > maxPathLength) { maxPathLength = offset maxPathLengthCount = 1 } else if (offset == maxPathLength) { maxPathLengthCount = maxPathLengthCount + 1 maxPathExample = "" for (i in 0...offset) { maxPathExample = maxPathExample + ((i % 5 == 0) ? "n " : " ") + part[i] } } var lastChar = part[offset - 1][-1] for (i in offset...part.count) { if (part[i][0] == lastChar) { var tmp = names[offset] names[offset] = names[i] names[i] = tmp search.call(names, offset + 1) names[i] = names[offset] names[offset] = tmp } } } for (i in 0...names.count) { var tmp = names[0] names[0] = names[i] names[i] = tmp search.call(names, 1) names[i] = names[0] names[0] = tmp } System.print("Maximum path length : %(maxPathLength)") System.print("Paths of that length : %(maxPathLengthCount)") System.print("Example path of that length : %(maxPathExample)")
Maximum path length : 23 Paths of that length : 1248 Example path of that length : machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune exeggcute emboar relicanth haxorus simisear registeel landorus seaking girafarig gabite emolga audino
Yabasic[edit]
Translation of: Phix
all$ = "audino bagon baltoy banette bidoof braviary bronzor carracosta charmeleon " all$ = all$ + "cresselia croagunk darmanitan deino emboar emolga exeggcute gabite " all$ = all$ + "girafarig gulpin haxorus heatmor heatran ivysaur jellicent jumpluff kangaskhan " all$ = all$ + "kricketune landorus ledyba loudred lumineon lunatone machamp magnezone mamoswine " all$ = all$ + "nosepass petilil pidgeotto pikachu pinsir poliwrath poochyena porygon2 " all$ = all$ + "porygonz registeel relicanth remoraid rufflet sableye scolipede scrafty seaking " all$ = all$ + "sealeo silcoon simisear snivy snorlax spoink starly tirtouga trapinch treecko " all$ = all$ + "tyrogue vigoroth vulpix wailord wartortle whismur wingull yamask" dim word$(1) lnames = token(all$, word$()) dim first(256), snext(lnames) for i = 1 to lnames ch = asc(left$(word$(i), 1)) if first(ch)=0 then first(ch) = i end if for j=i+1 to lnames if asc(left$(word$(j), 1))=ch then snext(i) = j break end if next next dim taken(lnames), best(lnames) sub try(ch, last, n) local nex, i nex = first(ch) while(nex <> 0) if taken(nex)=0 then taken(last) = nex taken(nex) = -1 try(asc(right$(word$(nex), 1)),nex,n+1) taken(last) = -1 taken(nex) = 0 end if nex = snext(nex) wend if n>maxn then bstart = tstart for i = 1 to lnames best(i) = taken(i) next maxn = n count = 1 elsif n=maxn then count = count + 1 end if end sub for i=1 to lnames tstart = i taken(i) = -1 try(asc(right$(word$(i), 1)),i,1) taken(i) = 0 next print "Runtime: ", peek("millisrunning")/1000, " seconds. Max length: ", maxn, ", found ", count, " of such, one of which is:n" do print word$(bstart), " "; bstart = best(bstart) if bstart = -1 break loop
zkl[edit]
This solution creates a hash table to all the possible word/following words to minimize search times.
No speed records were approached but 25sec seems fine for a one off walk the entire tree.
pokemon:=("audino bagon baltoy banette bidoof braviary " "bronzor carracosta charmeleon cresselia croagunk darmanitan deino " ... "whismur wingull yamask").split(); tree:=pokemon.pump(Dictionary(),'wrap(name){ //--> Hash("aB":("Bc","Bd",,,) ) T( name, pokemon.filter('wrap(nm){ name[-1]==nm[0] }) ) }); fcn maxPath([(a,_)]ab,[(c,_)]cd){ if(a>c) ab else cd } fcn walk(name,pathLen,path,tree){ //-->longest path for name names:=tree.find(name,T); tree[name]=T; // nuke cycle np:=names.reduce('wrap(np,name){ maxPath(np,walk(name,pathLen+1,String(path," ",name),tree)); },T(0,"")); tree[name]=names; if(np[0]>pathLen) return(np); return(pathLen,path); } pokemon.reduce('wrap(np,name){ maxPath(walk(name,1,name,tree),np) },T(0,"")) .println();
L(23,"machamp pinsir rufflet trapinch heatmor remoraid darmanitan nosepass starly yamask kricketune exeggcute emboar relicanth haxorus simisear registeel landorus seaking girafarig gabite emolga audino")
Fast and sharp word finder for fun and education
Crossword ModeFinds words containing given letters («w??d» — «word», «wood»). Enter a pattern. Use a question mark (?) or a dot (.) for unknown letters. |
Tap here for Xworder Mobile. xworder.com/m
Xworder provides word search tools designed to help you solve and compose crosswords Xworder features: Find words if you know some of the letters that it contains («w??d» — «word», «wood»).
Find words that can be built from the given set of letters («scrabble» — «laser»,
Find words and word combinations by rearranging all letters from the given set («anagram»
A fun game of building word chains by changing one letter at a time («break — bread
Switching between the Full and Limited word lists makes it easier to find what you |
© 2009 — 2011 Xworder.
How to use Xworder
Scrabble® is a registered trademark of Hasbro, Inc. in the USA and Canada.
Outside of the USA and Canada, the Scrabble® trademark is owned by Mattel, Inc.
My friend gave me a problem that he says is easy, but I can’t figure out a good algorithm to use to do it.
You are given an input of 100 random English words. You have to find the longest string of words where the last letter in one word matches the first letter in the next word. You can only use each word once.
For example, if you were given the words «cat», «dog», «that», the longest string you could make would be «cat -> that». If you were given the words «mouse», «moose», «unicorn», the longest string you could make would just be one word (since none of those words link). If you were given the words «bird», «dish», «harb», the longest string you could make would be «harb -> bird -> dish» (or «dish -> harb -> bird» or «bird -> dish -> harb»).
I came up with the idea of modeling this as a directed cyclic graph. Each node would just be a word, with vertices going to each word/node that started with the letter this word ended with.
+-------+ +------+
| cat |-----------| that |
+-------+ / +------+
| |
|/ |
+-------+ / |
| the |--------------+
+-------+
This problem appears to be a longest path search, which is NP-Hard.
Is there a better way to do it? Or even some sort of approximation algorithm that could be used? Or some way to exploit qualities of English to reduce the search space?
asked Aug 11, 2013 at 7:11
15
I think this is related to the longest path (LP) problem that you mentioned, but it’s a little different. The primary difference is that the LP problem has a higher degree of connectivity than what your suggested problem does. By restricting your connections to the last and first letters, you remove a large number of potential combinations.
Here’s how I would recommend tackling this one:
- For each word in the list, count the possible connections in and connections out.
- Discard any words that have 0 ins and 0 outs.
- Identify an initial set of «starter words» with the lowest numbers of ins and outs, and the outs must be greater than 0.
- Each starter word receives its own working copy of the ins / outs connection count. This forms the head of the chain.
- For each chain, identify a list of «next words» based upon:
- last letter of starter or previous word
- lowest number of of ins and outs connections (again, the outs must be greater than 0)
- For each
next word
, repeat step 5 until the chain terminates.
Keep in mind that:
-
You’ll need to keep track of the length of the chains and have some global mechanism to identify the longest chain.
-
You’ll also need to remove each word from the working copy of the connection counts in order to avoid a recursive loop.
-
At some point, your chain will terminate and you have to select a word with a 0 connection out count.
-
You may have to recalculate ins / outs as words are removed from the working lists. At first glance, I don’t think this will be necessary as the overall sets will be relatively small. If you scaled out to 1000 words, then having static counts may slow down the algorithm from converging.
I kind of saw this as a packing problem. To me, the connections in and out identify the shape to be packed. The lower the connections, the more odd the shape. The more odd the shape, the sooner I want to pack it as I perceived having decreasing odds of being able to pack an odd shape the later I got into the chain.
As an example:
{dog, gopher, alpha, cube, elegant, this, that, bart}
dog 0, 1
gopher 1, 0
alpha 0, 0
cube 0, 1
elegant 1, 2
this 3, 0
that 2, 1
bart 0, 2
//alpha is dropped with 0 in and 0 out.
//two candidates found: dog, cube
//chain 1
dog => gopher
//chain 2
cube => elegant => that => this
//Note 1: the following chain won't occur due to selection rules
//that takes priority over this because of output count
cube => elegant => this
//Note 2: this chain won't occur either due to selection rules
bart => that => this
answered Aug 14, 2013 at 17:54
5
If you make 26X26 matrix to represent directed graph of vertex as each alphabet and words as edge.
For example word — APPLE connect vertex A and E with edge directed from A to E.
Now the problem reduces to finding largest Eulerian trail (path which includes maximum number of edges, visiting each edge once wit possible repetition of vertices) in the graph.
One of the O(E) algorithm would be to start randomly from a pair of vertices.
Find a path between them. Than keep relaxing the path till it is possible.
update
@GlenH7 I solved a similar question on www.hackerearth/jda recently, there was relative marks with respect to best solution and I scored the highest marks with the following approch-
Given list of words. Find the longest chain that can be formed by them. A chain is valid if every word begin with a letter *ending at the ending of last word.
Approch =
1)make the graph of alphabets as vertices and words as edges.
In place of using multiple edges use one with weight equal to
number of edges.
2)find the strongly connected component of graph with
maximum edges. Temporarily discard other edges.
3)For each vertex make its indegree equal to its outdegree.
4)Now their exists eulerian circuit in graph. Find it.
5)Now in remaining graph(w.r.t orignal graph find the longest
trail with first vertex in chosen strongly connected
component. I think this is NP hard.
6)Include the above trail in Elerian circuit converting eulerian
circuit into trail.
Why — I accept that this question is most probably NP hard(guess, not mathematically speaking). But the above approach works best when there is a long list(1000+) of uniformly distributed words(i.e. not intended to be w.c. for above approach).
Let us assume that after converting given list to graph mentioned above, it luckily turns out to be a eulerian graph(see http://en.wikipedia.org/wiki/Eulerian_path for conditions), then without any doubt we can say that answer to above question is P and is actually the eulerian path in the graph(see http://www.graph-magics.com/articles/euler.php for a very simple approch to do so and see this to verify that your graph has single http://www.geeksforgeeks.org/strongly-connected-components/ and if not temporarily clean other small scc because eulerian path exists for single scc). Thus for not lucky cases(which are almost all cases) I try to convert them to lucky cases(i.e eulerian trail condition are fulfilled). How to do this? I tried do increasing depth search for irrelevant edges(the set of edges in a path staring from vertex with outdegree greater than indegree and ending at vertex with indegree greater than outdegree). Increasing depth search means that first I searched for all such set of one edge in path than two edges in path and so on. It may seem at first look that ith depth search would take O(nodes^i) thus total time complexity of O(nodes + nodes^2 + nodes^3 + ….) till it is a lucky case. But amortized analysis will revel it is O(edges). Once it is reduced lucky case find eulerian circuit.
Till here it was all polynomial time. This would give almost the best solution. But to further increase your solution(perfect solution is NP hard) try some greedy approach in remaining graph to find a long trail staring with one of vertices in chosen scc. Now add this to above found eulerian trail to further increase it.
answered Jun 25, 2014 at 20:25
1
Idea:
First, create two maps (hashes), say, S, and E, from alphabet letters to words; the first, S, maps starting letters to words, the second, E, does the same with ending letters.
E.g., if the dictionary is made of:
bird, dish, dog, harb
we have:
S:
a -> [ ]
b -> [ bird ]
c -> [ ]
d -> [ dish, dog ]
...
h -> [ harb ]
...
and,
E:
a -> [ ]
b -> [ harb ]
c -> [ ]
d -> [ bird ]
...
g -> [ dog ]
h -> [ dish ]
...
Next, using S and E for fast lookups, create a forest (set of trees), of the same size as the dictionary, with roots at each word, and not allowing a word to appear more than once in a tree — cache the trees’ depths as you construct them:
bird (depth: 2)
dish
harb
dog
dish (depth: 3)
harb
bird
dog
dog (depth: 0)
harb (depth: 2)
bird
dish
dog
Finally, iterate over the forest and find the tree(s) of greatest depth.
The solution(s) will be on the descendant axis of those trees.
E.g.,
dish / harb / bird / dog
above.
answered Mar 7, 2016 at 15:33
YSharpYSharp
8986 silver badges10 bronze badges
|
|
Recently Published
Quiz Scoreboard
More to Explore
Quiz From the Vault
Featured Blog Post
You Might Also Like…
Trending Topics
Showdown Scoreboard
More By:
suspence
Quiz | Plays | Rating | Category | Featured | Created |
---|---|---|---|---|---|
First and Last Letters III |
61,099 | 3.97 | Language | Aug 4, 2010 | |
Food Cubes |
332,387 | 4.73 | Miscellaneous | May 14, 2015 | |
First and Last Letters (5 Letter Words) |
89,023 | 4.62 | Language | Aug 4, 2010 | |
First and Last Letters III |
61,099 | 3.97 | Language | Aug 4, 2010 | |
5 Letter Word by Pattern |
57,013 | 3.70 | Language | Jun 30, 2010 | |
First and Last Letters IV |
51,574 | 4.49 | Language | Aug 4, 2010 |
Go to Creator’s Profile
Word Solver
Find Us Faster!
Bookmark This Page!
The Web’s Most Comprehensive Word Solver Site
This word solver is intended to help you unscramble letters to make words. This is part of our larger collection of puzzle solver tools. This particular word solver is designed to help you unscramble words for a word jumble puzzle, word puzzle, or scrabble game. In addition to this word unscrambler, we have other tools you can use to guess missing letters, solve boggle, and crack letter codes & ciphers. Works for
word scramble puzzles as well.
To use the word solver, enter your letters in the big friendly green box. The anagram solver behind the scenes will generate a list of words you can create from those letters. The results will be sorted by word length, in descending order (so 5 letter words, then 4 letter words, etc.) The word maker will usually give you multiple word options for each word length, if you are trying to fit a word into a word puzzle.
The solver supports wildcards (blank tile if you are playing a scrabble game). These are considered to be any possible letter and the letter in the result will be highlighted in the answer. This word maker should be enough to solve any word scramble puzzle.
We have created several versions of the jumbled word solver to help you with different challenges. These word finder tools are a powerful extension to our jumble word solver. Just another way to unscramble word ideas.
First, there is our scrabble solver. This scrabble word finder takes the same dictionary and anagram engine behind the word solver and adapts it into a scrabble cheat tool. The results are ranked by points, so the highest scoring words are shown first. The same engine also works as text twist solver or words with friends cheat (with different scrabble word points values). These anagram tools are enough to give you a commanding advantage in any word game you wish to deploy them in. No matter how much opponents jumble the rules.
Moving to Hangman and crossword puzzle games, we have a hangman solver which can double as a crossword tool. Enter the pattern from the crossword (letters you know and missing letters) and the word generator will present you with a list of options. Use the crossword clue to narrow your guess. If the clue isn’t enough, consider solving part of the word (via another word) and revisiting your results with fewer missing letters. This should cover your crossword solver needs. These use the same dictionary as the jumble word tools.
For advanced pattern matching (multiple word puzzles, substitution ciphers), we have other tools. Check out our word solver for wheel of fortune that can handle multiple words. Filter the multiple word answers by what sounds most natural in a sentence. (Assessing if an answer would fit into meaningful sentences is a great solution filter; that’s how many machine code-breakers work). If you are trying to match a pattern of unknown letters (eg. an 5 letter word, first and last letter the same), check out our cryptogram solver. This can be used to get help with cryptic crossword puzzles and similar advanced word puzzles. You can use this to solve puzzles where you know the vowel positions but not the other letters.
If you want to step up from our jumble solver think in more than one dimension, check out our boggle word solver. We have three versions — varying by the size of the word grid which you are searching. These are fed into our anagram engine to look for possible words. When you think about it, boggle is just a multi-dimensional jumble word puzzle. Same dictionary, different layout.
Word Game Design Applications (Word Maker Tools)
If you like solving word scramble puzzles, check out our new word scramble game . We did give a little additional help by color-coding the correct letters (green means you got it, red means keep trying). By the way, these are designed to make it hard to use a word solver to cheat. (the upside of being a wordsolver; you know how to block another wordsolver).
We actually do timed research on the human ability to solve scrambled word puzzles. Many of the best word solvers have worked out human versions of computer algorithms for searching a large set of valid word possibilities (dictionary x board) for fitting a word into an established puzzle space. This applies to all kinds of puzzles based on scrambled letters, ranging from the daily jumble to multi-dimensional scrabble games.
This type of thinking becomes extremely important when you’re working on game design and usability. Ever have someone give you a puzzle that was just impossible to crack? Even with a solver tools such as a scrabble word finder or similar word maker? Puzzle difficulty is actually a big deal for game designers. On one hand, you want to give the audience enough of a challenge so they don’t get bored. On the other, you want to avoid failure on the first pass, before someone gets invested in solving the app. Word finder tools are a mixed blessing in this environment, since they help with the latter but hurt you on the former. Sometimes the longest word isn’t the best word for a jumble answer.
We’ve actually done studies to «calibrate» the degree of difficulty in a puzzle game. This comes from showing jumbled word or cipher puzzles to people and watching how often they are solved. Along with testing changes in the word list (scrabble dictionary). You can give the audience hints (a piece of the pattern or a clue) that help them get started with solving the puzzle. Remember, if you make the jumbled letters too hard they’ll start using a solver tool (a word finder tool).
Bet you didn’t know there was all this science in word jumbles and crossword answers….
Other Word Solver Tools
We have other tools, particularly if you are trying to find words with these letters in them. This word scramble solver shows you
the word jumble solution, where you unscramble letters to make words. If you are trying to pattern match, we have
other tools (beyond the jumble solver). Use it to up your scrabble game.
If you are trying to guess a missing letter, check out our hangman solver.
The hangman solver also works fairly well as a crossword solver if you know some of the letters (pattern matching).
We also have another word solver for wheel of fortune that can handle multiple words.
If you are trying to match a pattern of unknown letters (eg. an 5 letter word, first and last letter the same), check
out our cryptogram helper.
Word Solver Games & Puzzles
If you like solving word scramble puzzles, check out our new
word scramble game. We did give a little additional help by color-coding the
correct letters (green means you got it, red means keep trying).
We’re more than just a word unscrambler site, by the way.
We’ve also got a cryptogram solving game. These puzzles are based
on substitution cipher codes, where each letter has been swapped with another letter.
Your job is to figure out which letter is which and decode the message. The game keeps
track of how long it takes to solve each puzzle and lets you know how your score
compares with others.
This article about how to solve
a cryptogram may also be helpful.
The next step up from using our word puzzle solver is outright code cracking.
If you like cracking codes, you may also find our article about pen and paper
cryptograph interesting. We walk through some of the building blocks of
manual cryptography (pre-World War I) and how these codes were broken. Those with a
technical bent may find our presentation on
breaking substitution ciphers via computer to be
interesting as well (to a wordsolver).
Please send all feedback, complaints, and lucrative sponsorship deals to admin@hanginghyena.com (page: wordsolver).
This Website is copyright © 2012 — 2022 Performance Ingenuity LLC. All Rights Reserved. We like cookies and use them on the site, per our Privacy Policy.
Search for a tool
Word Search
Tools to search words in a dictionary according to multiple criteria (advanced search on length, letters, start, middle, end, pattern, etc.)
Results
Word Search —
Tag(s) : Word Search
Share
dCode and more
dCode is free and its tools are a valuable help in games, maths, geocaching, puzzles and problems to solve every day!
A suggestion ? a feedback ? a bug ? an idea ? Write to dCode!
- Games and Solvers
- Word Search
- Word Search
Words Search for Word Game
This is a generic word search tool. On dCode, dozens of word game have a dedicated solver:
Words Search
Answers to Questions (FAQ)
What is a word search? (Definition)
The search for words means an exhaustive search in the dictionary according to given criteria such as the number of letters, the frequency of the word in the language or the presence of certain letters at certain positions in the word.
Reminder: This page is not always the most suitable for a word search following the rules of a specific word game. Use the search bar to find the appropriate solver.
How to search for words according to multiple criteria?
dCode allows to combine search of words starting with, words containing with, words ending with some letters (dedicated tools pages are available on dCode).
It is also possible to search for words that contain specific letters patterns, including the integration of joker letters (any letter)
Example: Search a word that contains the following letters DEF (in this order) in the middle of the word: UNDEFINED
Is is possible to force letters to be present, regardless of their order of appearance in the words (word search from scrambled letters in disorder).
Finally, it is possible to exclude some letters at the beginning, middle or end of a word or to sort results by length/size (selection of the number of letters).
The 3 criteria (start, middle, end) are exclusive, the containing part criterion only concerns the central letters of the word and does not apply to the first or the last letter.
How to search for words using regular expression?
For an even more precise search (but also more complex), dCode has a tool for searching words by regular expressions.
Source code
dCode retains ownership of the «Word Search» source code. Except explicit open source licence (indicated Creative Commons / free), the «Word Search» algorithm, the applet or snippet (converter, solver, encryption / decryption, encoding / decoding, ciphering / deciphering, translator), or the «Word Search» functions (calculate, convert, solve, decrypt / encrypt, decipher / cipher, decode / encode, translate) written in any informatic language (Python, Java, PHP, C#, Javascript, Matlab, etc.) and all data download, script, or API access for «Word Search» are not public, same for offline use on PC, mobile, tablet, iPhone or Android app!
Reminder : dCode is free to use.
Cite dCode
The copy-paste of the page «Word Search» or any of its results, is allowed as long as you cite dCode!
Exporting results as a .csv or .txt file is free by clicking on the export icon
Cite as source (bibliography):
Word Search on dCode.fr [online website], retrieved on 2023-04-14, https://www.dcode.fr/word-search
Summary
https://www.dcode.fr/word-search
© 2023 dCode — The ultimate ‘toolkit’ to solve every games / riddles / geocaching / CTF.
▲