-
Notifications
You must be signed in to change notification settings - Fork 0
/
20200709_shortest_palindrome.py
118 lines (95 loc) · 3.76 KB
/
20200709_shortest_palindrome.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
'''
2020-07-09
[from dailycodingproblem.com #34]
Given a string, find the palindrome that can be made by inserting the fewest
number of characters as possible anywhere in the word. If there is more than
one palindrome of minimum length that can be made, return the
lexicographically earliest one (the first one alphabetically).
For example, given the string "race", you should return "ecarace", since we
can add three letters to it (which is the smallest amount to make a
palindrome). There are seven other palindromes that can be made from "race"
by adding three letters, but "ecarace" comes first alphabetically.
As another example, given the string "google", you should return "elgoogle".
'''
# Solution Comment:
# Execution speed is slow if string length ~> 10 without repeated chars
# because of using permutations and combinations
from collections import defaultdict
from itertools import permutations, combinations
def is_palindrome(string):
mid_point = round(len(string) / 2)
if ''.join(reversed(string[:mid_point])) == string[-mid_point:]:
return True
return False
def has_original_sequence(candidate, string):
copy = list(string)
for i in range(len(candidate) - 1, -1, -1):
if len(copy) == 0:
break
if copy[-1] == candidate[i]:
copy.pop()
if len(copy) == 0:
return True
return False
def mirror(string):
single_pivot_mirror = ''.join(reversed(string)) + string[1:]
full_mirror = ''.join(reversed(string)) + string
return [single_pivot_mirror, full_mirror]
def permutate_mirror_and_check(candidate, string):
for p in sorted(permutations(candidate), reverse=True):
maybes = mirror(''.join(p))
maybes = [m for m in maybes
if (is_palindrome(m) and has_original_sequence(m, string))]
maybes.sort(key=lambda x: len(x))
if len(maybes) > 0:
return maybes[:1]
return []
def palindrome(string):
letters = defaultdict(int)
for s in string:
letters[s] += 1
base = ''.join([key for key in letters])
palindromes = permutate_mirror_and_check(base, string)
if len(palindromes) > 0:
return palindromes[0]
for i in range(2, max(letters.values()) + 1):
to_add = [key for key in letters if letters[key] >= i]
for j in range(1, len(to_add) + 1):
combos = (base + ''.join(c) for c in combinations(to_add, j))
batch = (permutate_mirror_and_check(c, string) for c in combos)
tops = (sorted(b, key=lambda x: (len(x), x))[:1] for b in batch)
for t in tops:
palindromes += t
palindromes.sort(key=lambda x: (len(x), x))
palindromes = palindromes[:1]
if len(palindromes) > 0:
return palindromes[0]
base += ''.join(to_add)
return ''
'''
# TESTS
# Words with no repeated letters
palindrome('race') == 'ecarace'
palindrome('pear') == 'pearaep'
palindrome('stare') == 'erastsare'
# Words with partial palindromes on the edges
palindrome('google') == 'elgoogle'
palindrome('alool') == 'aloola'
palindrome('vvdfaa') == 'aafdvvdfaa'
palindrome('aardvark') == 'akrardvdrarka'
# Words with partial palindromes inside
palindrome('timid') == 'dtimitd'
palindrome('ravage') == 'egravarge'
palindrome('effort') == 'etrofforte'
palindrome('leeves') == 'lesevesel'
# Words with partial palindromes everywhere
palindrome('ttimidd') == 'ddttimittdd'
# Longer words
palindrome('establish') == 'ehsilbatablishe'
palindrome('establishm') == 'emhsilbatablishme'
palindrome('establishme') == 'emhsilbatablishme'
palindrome('establishmen') == 'nemhsilbatablishmen'
palindrome('establishment') # TAKES TOO LONG TO EXECUTE
# Empty string returns empty string
palindrome('') == ''
'''