Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 35 additions & 53 deletions src/string/manacher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,74 +4,56 @@ pub fn manacher(s: String) -> String {
return s;
}

// MEMO: We need to detect odd palindrome as well,
// therefore, inserting dummy string so that
// we can find a pair with dummy center character.
// 1. Preprocessing: insert separators
let mut chars: Vec<char> = Vec::with_capacity(s.len() * 2 + 1);
for c in s.chars() {
chars.push('#');
chars.push(c);
}
chars.push('#');
let n = chars.len();

// List: storing the length of palindrome at each index of string
let mut length_of_palindrome = vec![1usize; chars.len()];
// Integer: Current checking palindrome's center index
let mut current_center: usize = 0;
// Integer: Right edge index existing the radius away from current center
let mut right_from_current_center: usize = 0;
// 2. p[i] represents the radius—how far it can expand symmetrically from the center in both directions.
let mut p = vec![0usize; n];
let mut center = 0;
let mut right = 0;

for i in 0..chars.len() {
// 1: Check if we are looking at right side of palindrome.
if right_from_current_center > i && i > current_center {
// 1-1: If so copy from the left side of palindrome.
// If the value + index exceeds the right edge index, we should cut and check palindrome later #3.
length_of_palindrome[i] = std::cmp::min(
right_from_current_center - i,
length_of_palindrome[2 * current_center - i],
);
// 1-2: Move the checking palindrome to new index if it exceeds the right edge.
if length_of_palindrome[i] + i >= right_from_current_center {
current_center = i;
right_from_current_center = length_of_palindrome[i] + i;
// 1-3: If radius exceeds the end of list, it means checking is over.
// You will never get the larger value because the string will get only shorter.
if right_from_current_center >= chars.len() - 1 {
break;
}
for i in 0..n {
// Mirror position
let mirror = 2 * center as isize - i as isize;
// Inherit the value from the mirror
if i < right {
if mirror >= 0 {
p[i] = p[mirror as usize].min(right - i);
} else {
// 1-4: If the checking index doesn't exceeds the right edge,
// it means the length is just as same as the left side.
// You don't need to check anymore.
continue;
p[i] = 0;
}
}

// Integer: Current radius from checking index
// If it's copied from left side and more than 1,
// it means it's ensured so you don't need to check inside radius.
let mut radius: usize = (length_of_palindrome[i] - 1) / 2;
radius += 1;
// 2: Checking palindrome.
// Need to care about overflow usize.
while i >= radius && i + radius <= chars.len() - 1 && chars[i - radius] == chars[i + radius]
{
length_of_palindrome[i] += 2;
radius += 1;
// Expand
while i + p[i] + 1 < n && i > p[i] && chars[i + p[i] + 1] == chars[i - p[i] - 1] {
p[i] += 1;
}

// Update the center and the right boundary
if i + p[i] > right {
center = i;
right = i + p[i];
}
}

// 3: Find the maximum length and generate answer.
let center_of_max = length_of_palindrome
.iter()
.enumerate()
.max_by_key(|(_, &value)| value)
.map(|(idx, _)| idx)
.unwrap();
let radius_of_max = (length_of_palindrome[center_of_max] - 1) / 2;
let answer = &chars[(center_of_max - radius_of_max)..=(center_of_max + radius_of_max)]
// 3. Find the maximum

let (center_of_max, &radius_of_max) = p.iter().enumerate().max_by_key(|&(_, &x)| x).unwrap();

// 4. Construct the answer
let start = center_of_max - radius_of_max;
let end = center_of_max + radius_of_max;
let answer: String = chars[start..=end]
.iter()
.collect::<String>();
.filter(|&&c| c != '#')
.cloned()
.collect();
answer.replace('#', "")
}

Expand All @@ -84,7 +66,7 @@ mod tests {
assert_eq!(manacher("babad".to_string()), "aba".to_string());
assert_eq!(manacher("cbbd".to_string()), "bb".to_string());
assert_eq!(manacher("a".to_string()), "a".to_string());

assert_eq!(manacher("abcba".to_string()), "abcba".to_string());
let ac_ans = manacher("ac".to_string());
assert!(ac_ans == *"a" || ac_ans == *"c");
}
Expand Down