diff --git a/Algorithms.Tests/Strings/ManachersAlgorithmTests.cs b/Algorithms.Tests/Strings/ManachersAlgorithmTests.cs
new file mode 100644
index 00000000..bc934c10
--- /dev/null
+++ b/Algorithms.Tests/Strings/ManachersAlgorithmTests.cs
@@ -0,0 +1,445 @@
+using Algorithms.Strings;
+using NUnit.Framework;
+using System;
+
+namespace Algorithms.Tests.Strings;
+
+///
+/// Comprehensive test suite for Manacher's Algorithm implementation.
+/// Tests cover various scenarios including:
+/// - Odd-length palindromes
+/// - Even-length palindromes
+/// - Single character strings
+/// - Empty strings
+/// - Strings with no palindromes longer than 1 character
+/// - Strings that are entirely palindromic
+/// - Multiple palindromes of the same length
+/// - Edge cases (null input, special characters)
+/// - Palindrome detection functionality
+/// - Detailed palindrome information retrieval.
+///
+public static class ManachersAlgorithmTests
+{
+ [Test]
+ public static void FindLongestPalindrome_WithOddLengthPalindrome_ReturnsCorrectPalindrome()
+ {
+ // Arrange: Classic example with odd-length palindrome "bab" or "aba"
+ string input = "babad";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Either "bab" or "aba" is valid (both have length 3)
+ Assert.That(result.Length, Is.EqualTo(3));
+ Assert.That(result == "bab" || result == "aba", Is.True);
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithEvenLengthPalindrome_ReturnsCorrectPalindrome()
+ {
+ // Arrange: String with even-length palindrome "abba"
+ string input = "cbbd";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should find "bb" (length 2)
+ Assert.That(result, Is.EqualTo("bb"));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithSingleCharacter_ReturnsSingleCharacter()
+ {
+ // Arrange: Edge case with single character
+ string input = "a";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Single character is a palindrome of itself
+ Assert.That(result, Is.EqualTo("a"));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithEmptyString_ReturnsEmptyString()
+ {
+ // Arrange: Edge case with empty string
+ string input = string.Empty;
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Empty string should return empty string
+ Assert.That(result, Is.EqualTo(string.Empty));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithNullString_ThrowsArgumentException()
+ {
+ // Arrange: Test defensive programming - null input validation
+ string? input = null;
+
+ // Act & Assert: Should throw ArgumentException for null input
+ Assert.Throws(() => ManachersAlgorithm.FindLongestPalindrome(input!));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithEntirePalindrome_ReturnsEntireString()
+ {
+ // Arrange: String that is entirely a palindrome
+ string input = "racecar";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should return the entire string
+ Assert.That(result, Is.EqualTo("racecar"));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithNoPalindromes_ReturnsSingleCharacter()
+ {
+ // Arrange: String with no palindromes longer than 1 character
+ string input = "abcdef";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should return a single character (any character is a palindrome)
+ Assert.That(result.Length, Is.EqualTo(1));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithLongPalindrome_ReturnsCorrectPalindrome()
+ {
+ // Arrange: String with a longer palindrome
+ string input = "forgeeksskeegfor";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should find "geeksskeeg" (length 10)
+ Assert.That(result, Is.EqualTo("geeksskeeg"));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithRepeatingCharacters_ReturnsCorrectPalindrome()
+ {
+ // Arrange: String with repeating characters
+ string input = "aaaa";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Entire string is a palindrome
+ Assert.That(result, Is.EqualTo("aaaa"));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithSpecialCharacters_ReturnsCorrectPalindrome()
+ {
+ // Arrange: String with special characters and spaces
+ string input = "A man, a plan, a canal: Panama";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should find a palindrome (note: this includes spaces and punctuation)
+ // The longest palindrome considering all characters is "anama"
+ Assert.That(result.Length, Is.GreaterThan(0));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithTwoCharacters_ReturnsCorrectResult()
+ {
+ // Arrange: Edge case with two identical characters
+ string input = "aa";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should return "aa"
+ Assert.That(result, Is.EqualTo("aa"));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithTwoDifferentCharacters_ReturnsSingleCharacter()
+ {
+ // Arrange: Edge case with two different characters
+ string input = "ab";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should return a single character
+ Assert.That(result.Length, Is.EqualTo(1));
+ }
+
+ [Test]
+ public static void FindLongestPalindromeWithDetails_ReturnsCorrectDetails()
+ {
+ // Arrange: Test the detailed version that returns index and length
+ string input = "babad";
+
+ // Act
+ var (palindrome, startIndex, length) = ManachersAlgorithm.FindLongestPalindromeWithDetails(input);
+
+ // Assert: Verify all components
+ Assert.That(length, Is.EqualTo(3));
+ Assert.That(palindrome.Length, Is.EqualTo(length));
+ Assert.That(input.Substring(startIndex, length), Is.EqualTo(palindrome));
+ Assert.That(palindrome == "bab" || palindrome == "aba", Is.True);
+ }
+
+ [Test]
+ public static void FindLongestPalindromeWithDetails_WithEmptyString_ReturnsZeroLength()
+ {
+ // Arrange
+ string input = string.Empty;
+
+ // Act
+ var (palindrome, startIndex, length) = ManachersAlgorithm.FindLongestPalindromeWithDetails(input);
+
+ // Assert
+ Assert.That(palindrome, Is.EqualTo(string.Empty));
+ Assert.That(startIndex, Is.EqualTo(0));
+ Assert.That(length, Is.EqualTo(0));
+ }
+
+ [Test]
+ public static void FindLongestPalindromeWithDetails_WithSingleCharacter_ReturnsCorrectDetails()
+ {
+ // Arrange
+ string input = "x";
+
+ // Act
+ var (palindrome, startIndex, length) = ManachersAlgorithm.FindLongestPalindromeWithDetails(input);
+
+ // Assert
+ Assert.That(palindrome, Is.EqualTo("x"));
+ Assert.That(startIndex, Is.EqualTo(0));
+ Assert.That(length, Is.EqualTo(1));
+ }
+
+ [Test]
+ public static void FindLongestPalindromeWithDetails_WithNullString_ThrowsArgumentException()
+ {
+ // Arrange
+ string? input = null;
+
+ // Act & Assert
+ Assert.Throws(() => ManachersAlgorithm.FindLongestPalindromeWithDetails(input!));
+ }
+
+ [Test]
+ public static void IsPalindrome_WithPalindromeString_ReturnsTrue()
+ {
+ // Arrange: Test palindrome detection with a valid palindrome
+ string input = "racecar";
+
+ // Act
+ bool result = ManachersAlgorithm.IsPalindrome(input);
+
+ // Assert: Should return true
+ Assert.That(result, Is.True);
+ }
+
+ [Test]
+ public static void IsPalindrome_WithNonPalindromeString_ReturnsFalse()
+ {
+ // Arrange: Test palindrome detection with a non-palindrome
+ string input = "hello";
+
+ // Act
+ bool result = ManachersAlgorithm.IsPalindrome(input);
+
+ // Assert: Should return false
+ Assert.That(result, Is.False);
+ }
+
+ [Test]
+ public static void IsPalindrome_WithSingleCharacter_ReturnsTrue()
+ {
+ // Arrange: Single character is always a palindrome
+ string input = "a";
+
+ // Act
+ bool result = ManachersAlgorithm.IsPalindrome(input);
+
+ // Assert: Should return true
+ Assert.That(result, Is.True);
+ }
+
+ [Test]
+ public static void IsPalindrome_WithEmptyString_ReturnsTrue()
+ {
+ // Arrange: Empty string is considered a palindrome
+ string input = string.Empty;
+
+ // Act
+ bool result = ManachersAlgorithm.IsPalindrome(input);
+
+ // Assert: Should return true
+ Assert.That(result, Is.True);
+ }
+
+ [Test]
+ public static void IsPalindrome_WithNullString_ThrowsArgumentException()
+ {
+ // Arrange
+ string? input = null;
+
+ // Act & Assert
+ Assert.Throws(() => ManachersAlgorithm.IsPalindrome(input!));
+ }
+
+ [Test]
+ public static void IsPalindrome_WithEvenLengthPalindrome_ReturnsTrue()
+ {
+ // Arrange: Even-length palindrome
+ string input = "abba";
+
+ // Act
+ bool result = ManachersAlgorithm.IsPalindrome(input);
+
+ // Assert: Should return true
+ Assert.That(result, Is.True);
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithNumericString_ReturnsCorrectPalindrome()
+ {
+ // Arrange: String with numbers
+ string input = "12321";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should return the entire string
+ Assert.That(result, Is.EqualTo("12321"));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithMixedCase_ReturnsCorrectPalindrome()
+ {
+ // Arrange: Mixed case string (case-sensitive palindrome check)
+ string input = "AbcbA";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should find "AbcbA" as it's case-sensitive
+ Assert.That(result, Is.EqualTo("AbcbA"));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithPalindromeAtStart_ReturnsCorrectPalindrome()
+ {
+ // Arrange: Palindrome at the start of the string
+ string input = "abaxyz";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should find "aba"
+ Assert.That(result, Is.EqualTo("aba"));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithPalindromeAtEnd_ReturnsCorrectPalindrome()
+ {
+ // Arrange: Palindrome at the end of the string
+ string input = "xyzaba";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should find "aba"
+ Assert.That(result, Is.EqualTo("aba"));
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithMultiplePalindromesOfSameLength_ReturnsOne()
+ {
+ // Arrange: Multiple palindromes of the same length
+ // "aba" at index 0 and "cdc" at index 3
+ string input = "abacdc";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should return one of them (the first one found)
+ Assert.That(result.Length, Is.EqualTo(3));
+ Assert.That(result == "aba" || result == "cdc", Is.True);
+ }
+
+ [Test]
+ public static void FindLongestPalindromeWithDetails_WithLongString_PerformsEfficiently()
+ {
+ // Arrange: Test with a longer string to verify O(n) performance
+ // Create a string with a palindrome in the middle
+ string input = new string('a', 1000) + "racecar" + new string('b', 1000);
+
+ // Act
+ var (_, _, length) = ManachersAlgorithm.FindLongestPalindromeWithDetails(input);
+
+ // Assert: Should find the longest palindrome (either the 'a's or 'b's or "racecar")
+ // The 1000 'a's form a palindrome
+ Assert.That(length, Is.GreaterThanOrEqualTo(7)); // At least "racecar"
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithVeryLongPalindrome_HandlesGracefully()
+ {
+ // Arrange: Test with a very long palindrome to verify O(n) performance
+ string input = new string('a', 10000);
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should correctly identify the entire string as a palindrome
+ Assert.That(result.Length, Is.EqualTo(10000));
+ Assert.That(result, Is.EqualTo(input));
+ }
+
+ [Test]
+ public static void IsPalindrome_WithVeryLongPalindrome_HandlesGracefully()
+ {
+ // Arrange: Test with a very long palindrome to verify performance
+ string input = new string('x', 5000);
+
+ // Act
+ bool result = ManachersAlgorithm.IsPalindrome(input);
+
+ // Assert: Should correctly identify as palindrome in O(n) time
+ Assert.That(result, Is.True);
+ }
+
+ [Test]
+ public static void FindLongestPalindrome_WithAlternatingCharacters_HandlesEdgeCases()
+ {
+ // Arrange: Test with alternating characters (minimal palindromes)
+ string input = "abababababababababababababababab";
+
+ // Act
+ string result = ManachersAlgorithm.FindLongestPalindrome(input);
+
+ // Assert: Should find at least a 3-character palindrome like "aba" or "bab"
+ Assert.That(result.Length, Is.GreaterThanOrEqualTo(3));
+ }
+
+ [Test]
+ public static void FindLongestPalindromeWithDetails_WithSpecialCharacters_HandlesCorrectly()
+ {
+ // Arrange: Test with special characters to ensure proper handling
+ string input = "abc!@#@!xyz";
+
+ // Act
+ var (palindrome, startIndex, length) = ManachersAlgorithm.FindLongestPalindromeWithDetails(input);
+
+ // Assert: Should find the palindrome with special characters
+ Assert.That(palindrome, Is.EqualTo("!@#@!"));
+ Assert.That(length, Is.EqualTo(5));
+ Assert.That(startIndex, Is.EqualTo(3));
+ }
+}
diff --git a/Algorithms/Strings/ManachersAlgorithm.cs b/Algorithms/Strings/ManachersAlgorithm.cs
new file mode 100644
index 00000000..5e535c03
--- /dev/null
+++ b/Algorithms/Strings/ManachersAlgorithm.cs
@@ -0,0 +1,393 @@
+namespace Algorithms.Strings;
+
+///
+/// Manacher's Algorithm is used to find the longest palindromic substring in linear time O(n).
+/// This algorithm is significantly more efficient than the naive O(n²) approach.
+///
+/// KEY CONCEPTS:
+/// 1. String Transformation: Inserts special characters to handle even/odd palindromes uniformly.
+/// 2. Palindrome Radius: For each position, stores how far the palindrome extends.
+/// 3. Center Expansion: Expands around each potential center to find palindromes.
+/// 4. Symmetry Property: Uses previously computed palindrome information to skip redundant checks.
+/// 5. Right Boundary Tracking: Maintains the rightmost boundary of any palindrome found.
+///
+/// WHY IT'S FAST:
+/// The algorithm achieves O(n) time by ensuring each character is examined at most twice:
+/// - Once when it's inside a known palindrome (using mirror property).
+/// - Once when expanding beyond the known boundary.
+///
+/// Reference: "A New Linear-Time On-Line Algorithm for Finding the Smallest Initial Palindrome of a String"
+/// by Glenn Manacher (1975), Journal of the ACM.
+///
+public static class ManachersAlgorithm
+{
+ ///
+ /// Finds the longest palindromic substring using Manacher's Algorithm.
+ ///
+ /// ALGORITHM STEPS:
+ /// 1. PREPROCESSING: Transform "abc" to "^#a#b#c#$" to handle even/odd palindromes uniformly.
+ /// - ^ and $ are sentinels that prevent boundary checks.
+ /// - # characters create uniform spacing.
+ ///
+ /// 2. INITIALIZATION: Set up tracking variables.
+ /// - palindromeRadii[i]: How far palindrome extends from position i.
+ /// - center: Center of rightmost palindrome found.
+ /// - rightBoundary: Right edge of rightmost palindrome.
+ ///
+ /// 3. MAIN LOOP: For each position i in transformed string.
+ /// a) If i is within rightBoundary, use mirror property:
+ /// - mirror = 2 * center - i (symmetric position).
+ /// - Start with min(palindromeRadii[mirror], rightBoundary - i).
+ /// b) Expand palindrome by comparing characters on both sides.
+ /// c) Update center and rightBoundary if palindrome extends further right.
+ /// d) Track the longest palindrome found.
+ ///
+ /// 4. EXTRACTION: Convert transformed coordinates back to original string.
+ ///
+ /// Time Complexity: O(n) - Each character examined at most twice.
+ /// Space Complexity: O(n) - For transformed string and radius array.
+ ///
+ /// The input string to search for palindromes.
+ /// The longest palindromic substring found in the input.
+ /// Thrown when the input string is null.
+ ///
+ /// Input: "babad".
+ /// Output: "bab" or "aba" (both are valid longest palindromes with length 3).
+ ///
+ /// Detailed Example:
+ /// Input: "abaxyz".
+ /// Transformed: "^#a#b#a#x#y#z#$".
+ /// Process finds "aba" at indices 1-3 with radius 3 in transformed string.
+ /// Maps back to indices 0-2 in original string.
+ /// Output: "aba".
+ ///
+ public static string FindLongestPalindrome(string input)
+ {
+ // Validate input
+ if (input == null)
+ {
+ throw new ArgumentException("Input string cannot be null.", nameof(input));
+ }
+
+ // Handle edge cases
+ if (input.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ if (input.Length == 1)
+ {
+ return input;
+ }
+
+ // STEP 1: Transform the string to handle even-length palindromes uniformly
+ // Example: "abc" becomes "^#a#b#c#$"
+ //
+ // WHY THIS WORKS:
+ // - Original "aba" (odd): Center is 'b' at index 1.
+ // - Original "abba" (even): No single center character.
+ // - Transformed "#a#b#b#a#": Both have clear centers (the middle #).
+ //
+ // SENTINELS (^ and $):
+ // - Prevent index out of bounds during expansion.
+ // - Never match any character, so expansion stops naturally.
+ string transformed = PreprocessString(input);
+ int n = transformed.Length;
+
+ // STEP 2: Initialize data structures
+
+ // palindromeRadii[i] = radius of palindrome centered at position i.
+ // Example: If transformed[i] is center of "#a#b#a#", radius = 3.
+ // (3 characters on each side match).
+ int[] palindromeRadii = new int[n];
+
+ // Track the rightmost palindrome boundary for optimization.
+ // center: Position of the palindrome that extends furthest right.
+ // rightBoundary: The rightmost index covered by that palindrome.
+ // These help us use symmetry to avoid redundant comparisons.
+ int center = 0;
+ int rightBoundary = 0;
+
+ // Track the longest palindrome found during the scan.
+ // maxLength: Radius of the longest palindrome.
+ // maxCenter: Position where the longest palindrome is centered.
+ int maxLength = 0;
+ int maxCenter = 0;
+
+ // STEP 3: Process each position in the transformed string.
+ // Skip first and last positions (sentinels ^ and $).
+ for (int i = 1; i < n - 1; i++)
+ {
+ // OPTIMIZATION: Use mirror property for positions within known palindrome
+ //
+ // If we have a palindrome centered at 'center' extending to 'rightBoundary':
+ // center - radius ... center ... center + radius
+ // mirror i
+ //
+ // The mirror position reflects i across the center:
+ // mirror = center - (i - center) = 2 * center - i.
+ int mirror = 2 * center - i;
+
+ // KEY INSIGHT: If i is within rightBoundary, we can use symmetry.
+ // The palindrome at position i might be similar to the one at mirror position.
+ if (i < rightBoundary)
+ {
+ // We can safely copy the radius from the mirror position, BUT:
+ // 1. palindromeRadii[mirror]: What we know from the mirror side.
+ // 2. rightBoundary - i: We can't assume anything beyond rightBoundary.
+ //
+ // Take the minimum because:
+ // - If mirror's palindrome fits within bounds, we can use it.
+ // - If it extends beyond, we only know up to rightBoundary.
+ palindromeRadii[i] = Math.Min(rightBoundary - i, palindromeRadii[mirror]);
+ }
+
+ // EXPANSION PHASE: Try to extend the palindrome further.
+ // We start from palindromeRadii[i] (not 0) to avoid redundant checks.
+ // This is why the algorithm is O(n) - we never re-check characters.
+ //
+ // Example: If palindromeRadii[i] = 2, we already know:
+ // transformed[i-2] == transformed[i+2] and
+ // transformed[i-1] == transformed[i+1].
+ // So we start checking at distance 3.
+ //
+ // The sentinels (^ and $) guarantee we never go out of bounds.
+ // Expansion stops naturally when characters don't match.
+ while (transformed[i + palindromeRadii[i] + 1] == transformed[i - palindromeRadii[i] - 1])
+ {
+ palindromeRadii[i]++;
+ }
+
+ // UPDATE TRACKING: If this palindrome extends further right than any before.
+ //
+ // WHY THIS MATTERS:
+ // By tracking the rightmost boundary, we can use the mirror property
+ // for future positions, avoiding redundant character comparisons.
+ //
+ // Example: If we find a large palindrome early, all positions within it
+ // can benefit from the symmetry property.
+ if (i + palindromeRadii[i] > rightBoundary)
+ {
+ center = i; // This position is now our reference center.
+ rightBoundary = i + palindromeRadii[i]; // Update the rightmost boundary.
+ }
+
+ // TRACK MAXIMUM: Remember the longest palindrome found so far.
+ // We need both the length and position to extract it later.
+ if (palindromeRadii[i] > maxLength)
+ {
+ maxLength = palindromeRadii[i]; // Radius of longest palindrome.
+ maxCenter = i; // Where it's centered in transformed string.
+ }
+ }
+
+ // STEP 4: Extract the longest palindrome from the original string.
+ //
+ // COORDINATE MAPPING:
+ // Transformed string has format: ^#a#b#c#$.
+ // Position in transformed -> Position in original: (pos - 1) / 2.
+ //
+ // Example: "aba" -> "^#a#b#a#$".
+ // - maxCenter = 4 (the middle 'b' in transformed).
+ // - maxLength = 3 (radius).
+ // - Original center = (4 - 1) / 2 = 1 (index of 'b' in "aba").
+ // - Start = (maxCenter - maxLength) / 2 = (4 - 3) / 2 = 0.
+ // - Length = maxLength = 3.
+ // - Result: input.Substring(0, 3) = "aba".
+ int start = (maxCenter - maxLength) / 2;
+ return input.Substring(start, maxLength);
+ }
+
+ ///
+ /// Finds the longest palindromic substring and returns detailed information.
+ /// This method provides more detailed information than FindLongestPalindrome,
+ /// including the exact starting position and length in the original string.
+ ///
+ /// USE CASE:
+ /// When you need to know WHERE the palindrome is located, not just what it is.
+ /// Useful for highlighting, replacing, or further processing the palindrome.
+ ///
+ /// The input string to search for palindromes.
+ ///
+ /// A tuple containing:
+ /// - The longest palindromic substring
+ /// - The starting index of the longest palindrome in the original string
+ /// - The length of the longest palindrome.
+ ///
+ /// Thrown when the input string is null.
+ ///
+ /// Input: "babad".
+ /// Output: (Palindrome: "bab", StartIndex: 0, Length: 3).
+ ///
+ public static (string Palindrome, int StartIndex, int Length) FindLongestPalindromeWithDetails(string input)
+ {
+ // Validate input
+ if (input == null)
+ {
+ throw new ArgumentException("Input string cannot be null.", nameof(input));
+ }
+
+ // Handle edge cases
+ if (input.Length == 0)
+ {
+ return (string.Empty, 0, 0);
+ }
+
+ if (input.Length == 1)
+ {
+ return (input, 0, 1);
+ }
+
+ // Apply the same algorithm as FindLongestPalindrome.
+ // (See detailed comments in that method for step-by-step explanation).
+ string transformed = PreprocessString(input);
+ int n = transformed.Length;
+ int[] palindromeRadii = new int[n];
+ int center = 0;
+ int rightBoundary = 0;
+ int maxLength = 0;
+ int maxCenter = 0;
+
+ // Main algorithm loop
+ for (int i = 1; i < n - 1; i++)
+ {
+ // Use mirror property if within known palindrome.
+ int mirror = 2 * center - i;
+
+ if (i < rightBoundary)
+ {
+ palindromeRadii[i] = Math.Min(rightBoundary - i, palindromeRadii[mirror]);
+ }
+
+ // Expand palindrome.
+ // Sentinels guarantee no out-of-bounds access.
+ while (transformed[i + palindromeRadii[i] + 1] == transformed[i - palindromeRadii[i] - 1])
+ {
+ palindromeRadii[i]++;
+ }
+
+ // Update rightmost boundary.
+ if (i + palindromeRadii[i] > rightBoundary)
+ {
+ center = i;
+ rightBoundary = i + palindromeRadii[i];
+ }
+
+ // Track maximum.
+ if (palindromeRadii[i] > maxLength)
+ {
+ maxLength = palindromeRadii[i];
+ maxCenter = i;
+ }
+ }
+
+ // Calculate the start index and extract the palindrome.
+ // Map from transformed coordinates to original string coordinates.
+ int startIndex = (maxCenter - maxLength) / 2;
+ string palindrome = input.Substring(startIndex, maxLength);
+
+ // Return all three pieces of information.
+ return (palindrome, startIndex, maxLength);
+ }
+
+ ///
+ /// Checks if the entire string is a palindrome using Manacher's Algorithm.
+ ///
+ /// EFFICIENCY:
+ /// - This approach: O(n) time using Manacher's algorithm.
+ /// - Naive approach: O(n) time for reversing + O(n) for comparison.
+ /// - Both are O(n), but this avoids creating a reversed copy.
+ ///
+ /// LOGIC:
+ /// If the longest palindrome in the string equals the string length,
+ /// then the entire string must be a palindrome.
+ ///
+ /// The string to check.
+ /// True if the entire string is a palindrome, false otherwise.
+ /// Thrown when the input string is null.
+ ///
+ /// Input: "racecar".
+ /// Output: true.
+ ///
+ public static bool IsPalindrome(string input)
+ {
+ if (input == null)
+ {
+ throw new ArgumentException("Input string cannot be null.", nameof(input));
+ }
+
+ // Strings of length 0 or 1 are always palindromes.
+ if (input.Length <= 1)
+ {
+ return true;
+ }
+
+ // Find the longest palindrome in the string.
+ // If it spans the entire string, then the string is a palindrome.
+ var (_, _, length) = FindLongestPalindromeWithDetails(input);
+ return length == input.Length;
+ }
+
+ ///
+ /// Preprocesses the input string by inserting special characters.
+ /// This transformation is the KEY to making Manacher's algorithm work efficiently.
+ ///
+ /// PROBLEM IT SOLVES:
+ /// - Odd-length palindromes ("aba") have a center character.
+ /// - Even-length palindromes ("abba") have a center between characters.
+ /// - Without transformation, we'd need separate logic for each case.
+ ///
+ /// SOLUTION:
+ /// Insert '#' between every character, making all palindromes odd-length.
+ ///
+ /// EXAMPLES:
+ /// - "aba" (odd) -> "^#a#b#a#$" (center is 'b').
+ /// - "abba" (even) -> "^#a#b#b#a#$" (center is '#' between the two 'b's).
+ ///
+ /// SENTINELS (^ and $):
+ /// - Placed at start and end.
+ /// - Never match any character (including each other).
+ /// - Automatically stop expansion without explicit boundary checks.
+ /// - Prevent IndexOutOfBoundsException.
+ ///
+ /// COORDINATE MAPPING:
+ /// - Original index i maps to transformed index (2*i + 2).
+ /// - Transformed index j maps to original index (j - 1) / 2.
+ ///
+ /// The original input string.
+ /// The transformed string with inserted special characters.
+ private static string PreprocessString(string input)
+ {
+ // Calculate the size of transformed string.
+ // Original: n characters.
+ // Transformed: ^ + # + (n chars with # between each) + # + $.
+ // Total: 1 + 1 + n + (n-1) + 1 + 1 = 2n + 3.
+ int n = input.Length;
+ char[] transformed = new char[n * 2 + 3];
+
+ // Place sentinels at boundaries.
+ transformed[0] = '^'; // Start sentinel (never matches anything).
+ transformed[n * 2 + 2] = '$'; // End sentinel (never matches anything).
+
+ // Build the transformed string: #a#b#c#.
+ // For input "abc":
+ // Position 0: ^ (sentinel).
+ // Position 1: # (separator).
+ // Position 2: a (input[0]).
+ // Position 3: # (separator).
+ // Position 4: b (input[1]).
+ // Position 5: # (separator).
+ // Position 6: c (input[2]).
+ // Position 7: # (separator).
+ // Position 8: $ (sentinel).
+ for (int i = 0; i < n; i++)
+ {
+ transformed[2 * i + 1] = '#'; // Separator before character.
+ transformed[2 * i + 2] = input[i]; // Original character.
+ }
+
+ transformed[n * 2 + 1] = '#'; // Final separator.
+
+ return new string(transformed);
+ }
+}
diff --git a/README.md b/README.md
index 8b499683..93ea4e25 100644
--- a/README.md
+++ b/README.md
@@ -218,6 +218,7 @@ find more than one implementation for the same objective but using different alg
* [WildCard Pattern Matching](./Algorithms/Strings/PatternMatching/WildCardMatcher.cs)
* [Z-block substring search](./Algorithms/Strings/PatternMatching/ZblockSubstringSearch.cs)
* [Longest Consecutive Character](./Algorithms/Strings/GeneralStringAlgorithms.cs)
+ * [Manacher's Algorithm](./Algorithms/Strings/ManachersAlgorithm.cs)
* [Palindrome Checker](./Algorithms/Strings/Palindrome.cs)
* [Get all permutations of a string](./Algorithms/Strings/Permutation.cs)
* [Other](./Algorithms/Other)