Skip to content

Commit 20adb31

Browse files
committed
feat: Add Manacher's Algorithm for finding the longest palindromic substring
- Implemented `manacher` function in `pydatastructs.strings.algorithms`. - Added unit tests for `manacher` in `test_algorithms.py`. - Updated `__all__` list to include `manacher` for public API access. Manacher's Algorithm provides an efficient O(n) solution for finding the longest palindromic substring in a given string. This addition enhances the string processing capabilities of the library.
1 parent ed88315 commit 20adb31

File tree

9 files changed

+79
-4
lines changed

9 files changed

+79
-4
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Algorithms
22
==========
33

4-
.. autofunction:: pydatastructs.find
4+
.. autofunction:: pydatastructs.find
5+
.. autofunction:: pydatastructs.manacher
Binary file not shown.
Binary file not shown.
Binary file not shown.

pydatastructs/strings/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
__all__.extend(trie.__all__)
1313

1414
from .algorithms import (
15-
find
15+
find,
16+
manacher
1617
)
1718

1819
__all__.extend(algorithms.__all__)

pydatastructs/strings/algorithms.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
Backend, raise_if_backend_is_not_python)
55

66
__all__ = [
7-
'find'
7+
'find',
8+
'manacher'
89
]
910

1011
PRIME_NUMBER, MOD = 257, 1000000007
@@ -83,6 +84,57 @@ def find(text, query, algorithm, **kwargs):
8384
%(algorithm))
8485
return getattr(algorithms, func)(text, query)
8586

87+
def manacher(text: str) -> str:
88+
"""
89+
Finds the longest palindromic substring in the given text using Manacher's algorithm.
90+
91+
Parameters
92+
==========
93+
text: str
94+
The input string in which to find the longest palindromic substring.
95+
96+
Returns
97+
=======
98+
str
99+
The longest palindromic substring found in the input text.
100+
101+
Examples
102+
========
103+
>>> manacher("abacdfgdcaba")
104+
'aba'
105+
>>> manacher("forgeeksskeegfor")
106+
'geeksskeeg'
107+
"""
108+
if not text:
109+
return ""
110+
111+
processed_text = "#" + "#".join(text) + "#"
112+
n = len(processed_text)
113+
lps = [0] * n
114+
center = 0
115+
right = 0
116+
max_len = 0
117+
max_center = 0
118+
119+
for i in range(n):
120+
if i < right:
121+
lps[i] = min(right - i, lps[2 * center - i])
122+
123+
while (i + lps[i] + 1 < n and i - lps[i] - 1 >= 0 and
124+
processed_text[i + lps[i] + 1] == processed_text[i - lps[i] - 1]):
125+
lps[i] += 1
126+
127+
if lps[i] > max_len:
128+
max_len = lps[i]
129+
max_center = i
130+
131+
if i + lps[i] > right:
132+
center = i
133+
right = i + lps[i]
134+
135+
start = (max_center - max_len) // 2
136+
end = start + max_len
137+
return text[start:end]
86138

87139
def _knuth_morris_pratt(text, query):
88140
if len(text) == 0 or len(query) == 0:

pydatastructs/strings/tests/test_algorithms.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from pydatastructs.strings import find
1+
from pydatastructs.strings import find, manacher
22

33
import random, string
44

@@ -14,6 +14,27 @@ def test_bm():
1414
def test_zf():
1515
_test_common_string_matching('z_function')
1616

17+
def test_manacher():
18+
"""
19+
Test cases for Manacher's Algorithm to find the longest palindromic substring.
20+
"""
21+
test_cases = [
22+
# (input_text, expected_longest_palindrome)
23+
("", ""), # Empty string
24+
("a", "a"), # Single character
25+
("abacdfgdcaba", "aba"), # Odd-length palindrome
26+
("abba", "abba"), # Even-length palindrome
27+
("forgeeksskeegfor", "geeksskeeg"), # Long palindrome
28+
("abcde", "a"), # No palindrome longer than 1
29+
("racecar", "racecar"), # Full string is a palindrome
30+
("abacaba", "abacaba"), # Overlapping palindromes
31+
("abacabacabb", "bacabacab"), # Complex case
32+
]
33+
34+
for text, expected in test_cases:
35+
result = manacher(text)
36+
assert result == expected, f"Failed for input: {text}. Expected: {expected}, Got: {result}"
37+
1738
def _test_common_string_matching(algorithm):
1839
true_text_pattern_dictionary = {
1940
"Knuth-Morris-Pratt": "-Morris-",
89 KB
Binary file not shown.
12.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)