use crate::error::{Result, StringError};
pub fn longest_palindrome(text: &str) -> Result<String> {
if text.is_empty() {
return Ok(String::new());
}
let transformed = preprocess(text);
let chars: Vec<char> = transformed.chars().collect();
let n = chars.len();
let mut p = vec![0; n]; let (mut center, mut right) = (0, 0);
for i in 1..(n - 1) {
let mirror = if i < center {
2 * center - i
} else {
i
};
if i < right {
p[i] = p[mirror].min(right - i);
}
while i + 1 + p[i] < n && i > p[i] {
let right_pos = i + 1 + p[i];
let left_pos = i.saturating_sub(1 + p[i]);
if chars[right_pos] != chars[left_pos] {
break;
}
p[i] += 1;
}
if i + p[i] > right {
center = i;
right = i + p[i];
}
}
let (mut max_center, mut max_len) = (0, 0);
for (i, &val) in p.iter().enumerate().skip(1).take(n - 2) {
if val > max_len {
max_center = i;
max_len = val;
}
}
let start = max_center
.checked_sub(1 + max_len)
.map(|x| x / 2)
.ok_or_else(|| StringError::invalid_input("Invalid palindrome position"))?;
if start + max_len > text.len() {
return Err(StringError::invalid_input("Invalid palindrome length"));
}
Ok(text[start..start + max_len].to_string())
}
fn preprocess(s: &str) -> String {
let mut result = String::with_capacity(s.len() * 2 + 3);
result.push('^');
for ch in s.chars() {
result.push('#');
result.push(ch);
}
result.push_str("#$");
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_string() {
assert_eq!(longest_palindrome("").unwrap(), "");
}
#[test]
fn test_single_char() {
assert_eq!(longest_palindrome("a").unwrap(), "a");
}
#[test]
fn test_odd_length_palindrome() {
let result = longest_palindrome("babad").unwrap();
assert!(result == "bab" || result == "aba");
}
#[test]
fn test_even_length_palindrome() {
assert_eq!(longest_palindrome("cbbd").unwrap(), "bb");
}
#[test]
fn test_all_same_chars() {
assert_eq!(longest_palindrome("aaaa").unwrap(), "aaaa");
}
#[test]
fn test_no_palindrome() {
let result = longest_palindrome("abcd").unwrap();
assert_eq!(result.len(), 1);
}
#[test]
fn test_complex_palindrome() {
assert_eq!(
longest_palindrome("forgeeksskeegfor").unwrap(),
"geeksskeeg"
);
}
}