Tell me more ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

If you like, write a program which sorts cities according to the rules of city name game.

  • Each name of the city should start from the last letter in the previous city name. E.g. Lviv -> v -> Viden -> n -> Neapolis -> s -> Sidney -> y -> Yokogama -> a -> Amsterdam -> m -> Madrid -> d -> Denwer

  • In the sorted list first letter of the first city and last letter of the last shouldn't match anything doesn't have to be the same letter.

  • You can assume city names have only letters.
  • Program output should have same capitalization as input

Example:

% ./script Neapolis Yokogama Sidney Amsterdam Madrid Lviv Viden Denwer
["Lviv", "Viden", "Neapolis", "Sidney", "Yokogama", "Amsterdam", "Madrid", "Denwer"]
share|improve this question
2  
Can we assume that there will always be a valid solution? – Gareth Jul 16 '12 at 16:00
@Gareth yes, you can – Artem Ice Jul 16 '12 at 16:02
the second rule - "[...] shouldn't match anything" - is it a requirement or just a statement saying that it's OK to have mismatch between the first and last letter? (ex: is a list like ["Viden" ... "Lviv"] invalid?) – w0lf Jul 16 '12 at 18:31
@w0lf by "shouldn't" I meant it doesn't have to, it is not compulsory. So your example is valid. – Artem Ice Jul 16 '12 at 18:39
Hint: If you want a nice solution, you can reduce this to the calculation of eulerian paths, where each letter is a vertex and each word is an edge. (For instance, Berlin is the edge BN) This is solvable in O(n), where n is the number of edges. – FUZxxl Jul 17 '12 at 22:56

11 Answers

up vote 8 down vote accepted

Ruby, 58 55 44 characters

p$*.permutation.find{|i|i*?,!~/(.),(?!\1)/i}

Yet another ruby implementation. Uses also case insensitive regex (as Ventero's old solution) but the test is done differently.

Previous version:

p$*.permutation.find{|i|(i*?,).gsub(/(.),\1/i,"")!~/,/}
share|improve this answer
Very nice! And I think you can get this down to 55 if you use !~ instead of negating the whole expression. – w0lf Jul 17 '12 at 13:33
That is smart regexp – Artem Ice Jul 17 '12 at 13:37
@w0lf Of course! How could I not think of that? – Howard Jul 17 '12 at 14:16

Python (162 141 124)

Brute force for the win.

from itertools import*
print[j for j in permutations(raw_input().split())if all(x[-1]==y[0].lower()for x,y in zip(j,j[1:]))]
share|improve this answer
1  
I think you can remove the &(j[0][0]!=j[-1][-1]) condition; see the question comments above. – w0lf Jul 16 '12 at 18:45
1  
124 from itertools import*;print[j for j in permutations(raw_input().split())if all(x[-1]==y[0].lower()for x,y in zip(j,j[1:]))] – Ev_genus Jul 16 '12 at 21:36
I'm trying to wrap my head around what's going on in this function. What exactly are j, x, y? How are they defined? I'm sorry if these questions are lame, I'm new to Python and would like to work with it some more. – Mike Dtrick Jul 17 '12 at 22:56
@MikeDtrick: j contains a permutation of the cities, which is generated with the permutations command. The big if on the end basically validates that for all values in j, the last letter of one value in j is the same as the first letter of the next value in j. Honestly, I don't know either what the zip does, zip works in mysterious ways. – beary605 Jul 17 '12 at 23:17
Okay, thank you for the explanation! +1 – Mike Dtrick Jul 18 '12 at 1:46
show 2 more comments

Ruby 1.9, 63 54 characters

New solution is based on Howard's solution:

p$*.permutation.max_by{|i|(i*?,).scan(/(.),\1/i).size}

This uses the fact that there'll always be a valid solution.

Old solution, based on w0lf's solution:

p$*.permutation.find{|i|i.inject{|a,e|a&&e[0]=~/#{a[-1]}/i&&e}}
share|improve this answer
Nice idea with the max_by. And your new version inspired myself for an even newer (and shorter) one. – Howard Jul 17 '12 at 14:57
@Howard Thanks! Your new solution is really awesome, will be difficult to beat that. ;) – Ventero Jul 17 '12 at 15:57

Ruby 74 72 104 103 71 70

p$*.permutation.find{|i|i.inject{|a,e|a[-1].casecmp(e[0])==0?e:?,}>?,}

Demo: http://ideone.com/MDK5c (in the demo I've used gets().split() instead of $*; I don't know if Ideone can simulate command-line args).

share|improve this answer
looks similar to my variant $*.permutation{|p|p p if p.inject(p[0][0]){|m,e|m.casecmp(e[0])==0?e[-1]:?_}>?_} but yours is 9 characters shorter! – Artem Ice Jul 16 '12 at 18:48
2  
p$*.permutation.find{|i|i.inject{|a,e|a&&e[0]=~/#{a[-1]}/i&&e}} is quite a bit shorter. A Ruby 1.8(!) solution which is even shorter: p$*.permutation.find{|i|i.inject{|a,e|a&&a[-1]-32==e[0]&&e}} – Ventero Jul 17 '12 at 0:03
@Ventero Using case-insensitive regex is a brilliant idea! Please post this as your own answer; I'm not worthy to use that. :) – w0lf Jul 17 '12 at 6:19
@Ventero the -32 solution is also very ingenious, but it relies on the fact that names start with an uppercase letter and end with a lowercase, which may not always be the case. – w0lf Jul 17 '12 at 6:22
@w0lf You're right, I thought I read in the specs that it would be the case, but obviously I'm mistaken. ;) – Ventero Jul 17 '12 at 12:01

Python, 113

Very similar to @beary605's answer, and even more brute-forced.

from random import*
l=raw_input().split()
while any(x[-1]!=y[0].lower()for x,y in zip(l,l[1:])):
 shuffle(l)
print l
share|improve this answer
1  
Woohoo, bogo-sort style! – beary605 Jul 17 '12 at 15:16

GolfScript, 78 characters

" ":s/.{1${1$=!},{:h.,{1$-1={1$0=^31&!{[1$1$]s*[\](\h\-c}*;}+/}{;.p}if}:c~;}/;

A first version in GolfScript. It also does a brute force approach. You can see the script running on the example input online.

share|improve this answer

Mathematica 236 chars

Define the list of cities:

d = {"Neapolis", "Yokogama", "Sidney", "Amsterdam", "Madrid", "Lviv", "Viden", "Denver"}

Find the path that includes all cities:

c = Characters; f = Flatten;
w = Outer[List, d, d]~f~1;
p = Graph[Cases[w, {x_, y_} /;x != y \[And] (ToUpperCase@c[x][[-1]]== c[y][[1]]) :> (x->y)]];
v = f[Cases[{#[[1]], #[[2]], GraphDistance[p, #[[1]], #[[2]]]} & /@  w, {_, _, Length[d] - 1}]];
FindShortestPath[p, v[[1]], v[[2]]]

Output:

{"Lviv", "Viden", "Neapolis", "Sidney", "Yokogama", "Amsterdam","Madrid", "Denver"}

The above approach assumes that the cities can be arranged as a path graph.


The graph p is shown below:

graph

share|improve this answer

C, 225

#define S t=v[c];v[c]=v[i];v[i]=t
#define L(x)for(i=x;i<n;i++)
char*t;f;n=0;main(int c,char**v){int i;if(!n)n=c,c=1;if(c==n-1){f=1;L(2){for(t=v[i-1];t[1];t++);if(v[i][0]+32-*t)f=n;}L(f)puts(v[i]);}else L(c){S;main(c+1,v);S;}}

Run with country names as the command line arguments

Note:

  • brute force generation of permutations
  • for checking it assumes that country names start with an upper case and end in lower case.
  • assumes there is only one answer
  • In C, assumes that the **v array of main() is writable
share|improve this answer

J, 69 65 60 59 54 characters

Somewhat off the pace.

{.l\:+/2=/\|:tolower;"2({.,{:)@>l=.(i.@!@#A.]);:1!:1[1

Example:

   {.l\:+/2=/\|:tolower;"2({.,{:)@>l=.(i.@!@#A.]);:1!:1[1
Neapolis Yokogama Sydney Amsterdam Madrid Lviv Viden Denwer
+----+-----+--------+------+--------+---------+------+------+
|Lviv|Viden|Neapolis|Sydney|Yokogama|Amsterdam|Madrid|Denwer|
+----+-----+--------+------+--------+---------+------+------+
share|improve this answer

K, 96

{m@&{&/(~).'+(,_1_1#'x),,-1_-1#'x}@'$m:{$[x=1;y;,/.z.s[x-1;y]{x,/:{x@&~x in y}[y;x]}\:y]}[#x;x]}

.

k){m@&{&/(~).'+(,_1_1#'x),,-1_-1#'x}@'$m:{$[x=1;y;,/.z.s[x-1;y]{x,/:{x@&~x in y}[y;x]}\:y]}[#x;x]}`Neapolis`Yokogama`Sidney`Amsterdam`Madrid`Lviv`Viden`Denver
Lviv Viden Neapolis Sidney Yokogama Amsterdam Madrid Denver
share|improve this answer

C#, 398

And here is C# with Linq 5 cents

IEnumerable<string>CityNameGame(string[]input){var cities=new List<string>(input);string lastCity=null;while(cities.Any()){var city=lastCity??cities.First();lastCity=cities.First(name=>string.Equals(city.Substring(city.Length-1),name.Substring(0,1),StringComparison.CurrentCultureIgnoreCase));cities.RemoveAll(name=>name==city||name==lastCity);yield return string.Format("{0}→{1}",city,lastCity);}}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.