Skip to content

Commit a286969

Browse files
committed
Added Manachers Algorithm implementation
1 parent b8cc5e3 commit a286969

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

pygorithm/string/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
from . import pangram
66
from . import isogram
77
from . import palindrome
8+
from . import manacher_algorithm
89

910
__all__ = [
1011
'anagram',
1112
'pangram',
1213
'isogram',
14+
'manacher_algorithm',
1315
'palindrome'
1416
]
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'''
2+
Author: OMKAR PATHAK
3+
Created at: 27th August 2017
4+
'''
5+
6+
7+
def manacher(string):
8+
"""
9+
Computes length of the longest palindromic substring centered on each char
10+
in the given string. The idea behind this algorithm is to reuse previously
11+
computed values whenever possible (palindromes are symmetric).
12+
13+
Example (interleaved string):
14+
i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
15+
s # a # b # c # q # q # q # q # q # q # x # y #
16+
P 0 1 0 1 0 1 0 1 2 3 4 5 6 5 4 ?
17+
^ ^ ^ ^
18+
mirror center current right
19+
20+
We're at index 15 wondering shall we compute (costly) or reuse. The mirror
21+
value for 15 is 9 (center is in 12). P[mirror] = 3 which means a palindrome
22+
of length 3 is centered at this index. A palindrome of same length would be
23+
placed in index 15, if 15 + 3 <= 18 (right border of large parlindrome
24+
centered in 12). This condition is satisfied, so we can reuse value from
25+
index 9 and avoid costly computation.
26+
"""
27+
if type(string) is not str:
28+
raise TypeError("Manacher Algorithm only excepts strings, not {}".format(str(type(string))))
29+
30+
# Get the interleaved version of a given string. 'aaa' --> '^#a#a#a#$'.
31+
# Thanks to this we don't have to deal with even/odd palindrome
32+
# length problem.
33+
string_with_bounds = '#'.join('^{}$'.format(string))
34+
length = len(string_with_bounds)
35+
P = [0] * length
36+
center = right = 0
37+
38+
for i in range(1, length - 1):
39+
P[i] = (right > i) and min(right - i, P[2 * center - i])
40+
41+
# Attempt to expand palindrome centered at i
42+
while string_with_bounds[i + 1 + P[i]] == string_with_bounds[i - 1 - P[i]]:
43+
P[i] += 1
44+
45+
# If palindrome centered at i expand past R,
46+
# adjust center based on expanded palindrome.
47+
if i + P[i] > right:
48+
center, right = i, i + P[i]
49+
50+
# Find the maximum element in P and return the string
51+
maxLen, centerIndex = max((n, i) for i, n in enumerate(P))
52+
return string[(centerIndex - maxLen)//2: (centerIndex + maxLen)//2]
53+
54+
def get_code():
55+
"""
56+
returns the code for the manacher's algorithm
57+
:return: source code
58+
"""
59+
return inspect.getsource(manacher)

0 commit comments

Comments
 (0)