50 lines
1.3 KiB
Python
50 lines
1.3 KiB
Python
"""Manacher's algorithm: longest palindromic substring in O(n)."""
|
|
from __future__ import annotations
|
|
|
|
|
|
def longest_palindrome(s: str) -> str:
|
|
if s == "":
|
|
return ""
|
|
# Transform: insert sentinels so every palindrome has odd length.
|
|
t = "^#" + "#".join(s) + "#$"
|
|
n = len(t)
|
|
p = [0] * n
|
|
c = r = 0
|
|
for i in range(1, n - 1):
|
|
mirror = 2 * c - i
|
|
if i < r:
|
|
p[i] = min(r - i, p[mirror])
|
|
# expand
|
|
while t[i + p[i] + 1] == t[i - p[i] - 1]:
|
|
p[i] += 1
|
|
if i + p[i] > r:
|
|
c = i
|
|
r = i + p[i]
|
|
# find center with max radius
|
|
center = 1
|
|
for i in range(1, n - 1):
|
|
if p[i] > p[center]:
|
|
center = i
|
|
start = (center - p[center]) // 2
|
|
return s[start:start + p[center]]
|
|
|
|
|
|
def all_palindromic_substrings(s: str) -> list[str]:
|
|
"""Return all distinct palindromic substrings (may be large)."""
|
|
res = set()
|
|
n = len(s)
|
|
for i in range(n):
|
|
# odd
|
|
l = r = i
|
|
while l >= 0 and r < n and s[l] == s[r]:
|
|
res.add(s[l:r + 1])
|
|
l -= 1
|
|
r += 1
|
|
# even
|
|
l, r = i, i + 1
|
|
while l >= 0 and r < n and s[l] == s[r]:
|
|
res.add(s[l:r + 1])
|
|
l -= 1
|
|
r += 1
|
|
return sorted(res)
|