-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcodechallenge_019.py
223 lines (190 loc) · 6.16 KB
/
codechallenge_019.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
'''
Date:01/03/2019
Problem description:
===================
This problem was asked by Quora.
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".
Algorithm:
==========
Input: A string
Output A palindrome string
Pseudo code:
1. Check for valid input
2. Check if the string has consecutively repeated chars
If yes. Handle the complex case of 'oo' in the word 'google' (step3 below).
Else, make the smallest amount of work in making a palindrome. i.e.
copy the string into another, reverse the order of the new string and pop the last item
prepend the new string to the original string and we have ourselve a palindrome.
3. Handling the complex case:
3.1 Iterate the string from middle index toward the end
3.2 compare the mirrored characters, prepend mirrored character if not a match.
If not yet a palindrome, keep prependng a mirrored character to the beginning of the string and
go back to check it again. This ahould be verry intuitive once you had pictured it in your head.
Further thoughts:
================
What happens if input is already a palindrome?
'''
import re
#
# Check if the string has consecutively repeated chars
# return True if there are repeated chars in instr
#
def isRepeatedChars(instr):
# e.g. See the interactive python result here:
'''
>>> instr = 'google'
>>> repchars = [s[1] + s[0] for s in re.findall(r'(.)(\1*)', instr)]
>>> repchars
['g', 'oo', 'g', 'l', 'e']
'''
repchars = [s[1] + s[0] for s in re.findall(r'(.)(\1*)', instr)]
try:
# locate the consecutively repeated characters
midstr = [i for i in repchars if len(i) > 1][0]
return (len(midstr) > 0)
except IndexError:
return False
#
# use re.findall() method to find middle index+offset
# this allows us to deal with words such as 'oo' in 'google'
# return start and end indices of the consecutively repeated characters substring:
# Starting index of the repeated character
# Ending index of the repeated character
# e.g. return None, None
# return 1, 2
#
def idxOfRepeatedChars(instr):
# Find repeated chracters by converting string into an array of characters
repchars = [s[1] + s[0] for s in re.findall(r'(.)(\1*)', instr)]
try:
# locate the consecutively repeated characters
# it should be where length of item is greater than one
# e.g. if instr='google', midstr should be 'oo'
midstr = [i for i in repchars if len(i) > 1][0]
if len(midstr) > 0:
# find index of this midstr above and its offset
mididx = instr.index(midstr)
return int(mididx), int(mididx+len(midstr)-1)
else:
return None, None
except IndexError:
return None, None
#
# decorator function
#
def palindrome(func):
def inner(*args):
instr = args[0]
mididx = len(instr)//2
# iterate from edge toward middle
ret = False
for i in range(mididx):
if instr[i] == instr[-1 + (i * -1)]:
ret = True
else:
return False
return ret
return inner
#
# validate that the string is a palindrome
#
@palindrome
def isValidPalindrome(instr):
if len(instr) == 0:
return False
# remove any extra white space tat both ends
instr.strip()
return palindrome(instr)
#
# Create a palindrome string
#
def getPalindrome(instr):
if not instr.isalpha():
return "Invalid input. Not an alphabetic string"
# in case it is already a palindrome. just return it!
if isValidPalindrome(instr):
return instr
if isRepeatedChars(instr):
# found consecutively repeated characters such as 'oo' in 'google'
# deal with the offset index
startidx, endidx = idxOfRepeatedChars(str(instr))
#print("DBUG: instr:{} startidx:{} endidx:{}".format(instr, startidx, endidx))
# iterate from middle toward edge
# e.g.
# 0 1 2 3 4 5
# g o o g l e
# startidx:1, endidx:2
count = 1
for i in range(endidx+1, len(instr)+1, 1):
if instr[i] == instr[startidx - count]:
pass
else:
instr = instr[:0] + instr[i] + instr[0:]
count+=1
else: # remaining cases such as word 'race'
halfidx = len(instr)//2
# iterate from edge toward middle
# e.g.
# from 0 .. halfidx
flipchars = list(instr)
flipchars.reverse()
flipchars.pop()
flipstr = ''.join(flipchars)
instr = instr[:0] + flipstr + instr[0:]
#print(instr)
return instr
#
# unittest fucntion
#
def test_palindrome():
A='google'
assert getPalindrome(A) == 'elgoogle'
A = 'race'
assert getPalindrome(A) == 'ecarace'
#
# client function
#
if __name__ == '__main__':
A='google'
print("Test1:\nInput: {}".format(A))
PalindStr = getPalindrome(A)
print("Output: {}".format(PalindStr))
print("Validate palindrome string: \'{}\' as {}.".format(PalindStr, isValidPalindrome(PalindStr)))
A='race'
print("\nTest2:\nInput: {}".format(A))
PalindStr = getPalindrome(A)
print("Output: {}".format(PalindStr))
print("Validate palindrome string: \'{}\' as {}.".format(PalindStr, isValidPalindrome(PalindStr)))
A='ABBA'
print("\nTest3:\nInput: {}".format(A))
PalindStr = getPalindrome(A)
print("Output: {}".format(PalindStr))
print("Validate palindrome string: \'{}\' as {}.".format(PalindStr, isValidPalindrome(PalindStr)))
A='bananas'
print("\nTest3:\nInput: {}".format(A))
PalindStr = getPalindrome(A)
print("Output: {}".format(PalindStr))
print("Validate palindrome string: \'{}\' as {}.".format(PalindStr, isValidPalindrome(PalindStr)))
'''
Run-time output:
================
linux1@sles12sp3:/data/devel/py-src/DailyCodingChallenge> python codechallenge_019.py
Test1:
Input: google
Output: elgoogle
Validate palindrome string: 'elgoogle' as True.
Test2:
Input: race
Output: ecarace
Validate palindrome string: 'ecarace' as True.
Test3:
Input: ABBA
Output: ABBA
Validate palindrome string: 'ABBA' as True.
'''