I have a list of nations, and I want to have the longest path of nations where each country chosen must begin with the same letter that ended the previous element
nations = ['albania','andorra','austria','belarus','belgium','bosnia and herzegovina',
'bulgaria','croatia','czech republic','denmark','estonia',
'finland','france','germany','greece','hungary',
'iceland','ireland','italy','latvia','liechtenstein','lithuania','luxembourg',
'macedonia','malta','moldova','monaco','montenegro','netherlands',
'norway','poland','portugal','romania','russia',
'san marino','serbia','slovakia','slovenia','spain','sweden', 'switzerland',
'ukraine','united kingdom','vatican city']
chain('spain')
>>>['spain', 'netherlands', 'slovenia', 'andorra', 'austria', 'albania']
I tried this way, but it doesn't work
def chain(naz):
initial = naz[-1]
initials=[]
res = set()
res.add(naz)
for i in nations:
if i.startswith(initial):
initials.append(i)
for j in initials:
nations.remove(j)
res.add(j)
chain(j)
return res
Any suggestion?
According to the source code, the maximum size of a list is PY_SSIZE_T_MAX/sizeof(PyObject*) . On a regular 32bit system, this is (4294967295 / 2) / 4 or 536870912. Therefore the maximum size of a python list on a 32 bit system is 536,870,912 elements.
Iterate over the characters of the string. Check if the current character encountered is a space or end of the string is reached. If the current character is found to be so, update the maximum length of a word obtained. Finally, print the longest word obtained using substr() method.
I've also gone for recursive descent. Not sure if dynamic programming is a good fit for this as the list is modified as we go. Slightly more compact and doesn't need start removed from the list before calling chain. :-)
def chain(start, countries):
remaining = list(countries)
del remaining[remaining.index(start)]
possibles = [x for x in remaining if x[:1] == start[-1:]]
maxchain = []
for c in possibles:
l = chain(c, remaining)
if len(l) > len(maxchain):
maxchain = l
return [start] + maxchain
Call like this. :-)
>>> chain('spain', nations)
['spain', 'netherlands', 'serbia', 'albania', 'andorra', 'austria']
Here are some comments:
while
somewherei.startswith(initial)
is true only if i starts with the whole initial word. You probably don't want thisnations
is a global variable, which is badedit
The bug described in your comment may occur because your recurcive call is inside the j loop. The recurcive call may remove elements for the nations, which may also exist in initials. So you are trying to remove them more than once, which raises an exception. You probably mean to put chain(j)
outside the loop (and maybe use its return value?)
As a side note, your problem is NP-complete (meaning it has no "fast" polynomial-time solution.) It's solvable for small problem sizes, but it gets very difficult very quickly.
Your problem can be thought of as the longest-path problem on a directed graph.
w1
and w2
, draw an edge w1 -> w2
if the last letter of w1
is the same as the first letter of w2
.w2->w1
if w2
s last letter is the same as w1
s first letter.Here's an example graph for a list of fruits and vegetables: Apple, banana, eggplant, kiwifruit, orange, oregano, tangerine, zucchini
.
This graph may contain cycles (for example, this graph has a cycle eggplant -> tangerine -> eggplant -> tangerine....
. The longest path problem for directed graphs containing cycles is NP-complete. Therefore there is no polynomial-time solution for this problem.
This doesn't mean you can't do better than brute force. There's a dynamic programming algorithm that reduces the complexity from O(n!)
(factorial, very bad) to O(n^2 * 2^n)
(superexponential, still bad, but better than factorial.)
this is a naive recursive approach... i feel like you could use dynamic programming and it would be better
def chain(start,options):
#start is the starting word
#options are the words left
next_options = [country for country in options if country[0] == start[-1]]
#if theres no options, return the single
if not next_options:
return [start]
#otherwise, return best chain out of the next option
best_chain = None
longest_chain = 0
for option in next_options:
new_options = options[:]
new_options.remove(option)
possible_chain = chain(option,new_options)
if len(possible_chain) > longest_chain:
best_chain = possible_chain
longest_chain = len(possible_chain)
return [start] + best_chain
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With