Skip to content

Commit dacf1d0

Browse files
authored
Implement Manacher's algorithm (TheAlgorithms#1721)
* manacher's algorithm updated
1 parent 74a7b5f commit dacf1d0

File tree

1 file changed

+70
-24
lines changed

1 file changed

+70
-24
lines changed

strings/manacher.py

+70-24
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,18 @@
1-
# calculate palindromic length from center with incrementing difference
2-
def palindromic_length(center, diff, string):
3-
if (
4-
center - diff == -1
5-
or center + diff == len(string)
6-
or string[center - diff] != string[center + diff]
7-
):
8-
return 0
9-
return 1 + palindromic_length(center, diff + 1, string)
10-
11-
121
def palindromic_string(input_string):
132
"""
14-
Manacher’s algorithm which finds Longest Palindromic Substring in linear time.
3+
>>> palindromic_string('abbbaba')
4+
'abbba'
5+
>>> palindromic_string('ababa')
6+
'ababa'
7+
8+
Manacher’s algorithm which finds Longest palindromic Substring in linear time.
159
1610
1. first this convert input_string("xyx") into new_string("x|y|x") where odd
1711
positions are actual input characters.
18-
2. for each character in new_string it find corresponding length and store,
19-
a. max_length
20-
b. max_length's center
21-
3. return output_string from center - max_length to center + max_length and remove
22-
all "|"
12+
2. for each character in new_string it find corresponding length and store the length
13+
and l,r to store previously calculated info.(please look the explanation for details)
14+
15+
3. return corresponding output_string by removing all "|"
2316
"""
2417
max_length = 0
2518

@@ -33,25 +26,78 @@ def palindromic_string(input_string):
3326
# append last character
3427
new_input_string += input_string[-1]
3528

29+
# we will store the starting and ending of previous furthest ending palindromic substring
30+
l, r = 0, 0
31+
32+
# length[i] shows the length of palindromic substring with center i
33+
length = [1 for i in range(len(new_input_string))]
34+
3635
# for each character in new_string find corresponding palindromic string
3736
for i in range(len(new_input_string)):
37+
k = 1 if i > r else min(length[l + r - i] // 2, r - i + 1)
38+
while (
39+
i - k >= 0
40+
and i + k < len(new_input_string)
41+
and new_input_string[k + i] == new_input_string[i - k]
42+
):
43+
k += 1
44+
45+
length[i] = 2 * k - 1
3846

39-
# get palindromic length from i-th position
40-
length = palindromic_length(i, 1, new_input_string)
47+
# does this string is ending after the previously explored end (that is r) ?
48+
# if yes the update the new r to the last index of this
49+
if i + k - 1 > r:
50+
l = i - k + 1
51+
r = i + k - 1
4152

4253
# update max_length and start position
43-
if max_length < length:
44-
max_length = length
54+
if max_length < length[i]:
55+
max_length = length[i]
4556
start = i
4657

4758
# create that string
48-
for i in new_input_string[start - max_length : start + max_length + 1]:
59+
s = new_input_string[start - max_length // 2 : start + max_length // 2 + 1]
60+
for i in s:
4961
if i != "|":
5062
output_string += i
5163

5264
return output_string
5365

5466

5567
if __name__ == "__main__":
56-
n = input()
57-
print(palindromic_string(n))
68+
import doctest
69+
70+
doctest.testmod()
71+
72+
"""
73+
...a0...a1...a2.....a3......a4...a5...a6....
74+
75+
consider the string for which we are calculating the longest palindromic substring is shown above where ...
76+
are some characters in between and right now we are calculating the length of palindromic substring with
77+
center at a5 with following conditions :
78+
i) we have stored the length of palindromic substring which has center at a3 (starts at l ends at r) and it
79+
is the furthest ending till now, and it has ending after a6
80+
ii) a2 and a4 are equally distant from a3 so char(a2) == char(a4)
81+
iii) a0 and a6 are equally distant from a3 so char(a0) == char(a6)
82+
iv) a1 is corresponding equal character of a5 in palindrome with center a3 (remember that in below derivation of a4==a6)
83+
84+
now for a5 we will calculate the length of palindromic substring with center as a5 but can we use previously
85+
calculated information in some way?
86+
Yes, look the above string we know that a5 is inside the palindrome with center a3 and previously we have
87+
have calculated that
88+
a0==a2 (palindrome of center a1)
89+
a2==a4 (palindrome of center a3)
90+
a0==a6 (palindrome of center a3)
91+
so a4==a6
92+
93+
so we can say that palindrome at center a5 is at least as long as palindrome at center a1
94+
but this only holds if a0 and a6 are inside the limits of palindrome centered at a3 so finally ..
95+
96+
len_of_palindrome__at(a5) = min(len_of_palindrome_at(a1), r-a5)
97+
where a3 lies from l to r and we have to keep updating that
98+
99+
and if the a5 lies outside of l,r boundary we calculate length of palindrome with bruteforce and update
100+
l,r.
101+
102+
it gives the linear time complexity just like z-function
103+
"""

0 commit comments

Comments
 (0)