Last active
April 25, 2019 12:00
-
-
Save baratgabor/78c5c902cd130977daca7aa5058d598e to your computer and use it in GitHub Desktop.
Manacher's algorithm for finding palindromes, in C#, refactored to separate the pre-processing, and to return IEnumerable<string> of palindromes of specified min and max length. Exposes static methods for instant queries, and instance methods for pre-processing a string only once and then querying it multiple times.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ManachersPalindrome | |
{ | |
private readonly char[] s2; | |
private readonly int[] p; | |
public static ManachersPalindrome Build(string s) | |
=> new ManachersPalindrome(s); | |
private ManachersPalindrome(string s) | |
{ | |
if (string.IsNullOrEmpty(s)) | |
throw new ArgumentException($"Parameter '{nameof(s)}' cannot be empty or null."); | |
s2 = AddBoundaries(s.ToArray()); | |
p = PreProcess(s2); | |
} | |
public string LongestPalindrome() | |
=> LongestPalindrome(s2, p); | |
public IEnumerable<string> AllPalindromes(int minLength, int maxLength = int.MaxValue) | |
=> AllPalindromes(s2, p, minLength, maxLength); | |
public static string FindLongestPalindrome(string s) | |
{ | |
if (string.IsNullOrEmpty(s)) | |
throw new ArgumentException($"Parameter '{nameof(s)}' cannot be empty or null."); | |
var s2 = AddBoundaries(s.ToArray()); | |
return LongestPalindrome(s2, PreProcess(s2)); | |
} | |
public static IEnumerable<string> FindAllPalindromes(string s, int minLength, int maxLength = int.MaxValue) | |
{ | |
if (string.IsNullOrEmpty(s)) | |
throw new ArgumentException($"Parameter '{nameof(s)}' cannot be empty or null."); | |
var s2 = AddBoundaries(s.ToCharArray()); | |
return AllPalindromes(s2, PreProcess(s2), minLength, maxLength); | |
} | |
private static string LongestPalindrome(char[] s2, int[]p) | |
{ | |
int len = 0, c = 0, s2len = s2.Length; | |
for (int i = 1; i < s2len; i++) | |
{ | |
if (len < p[i]) | |
{ | |
len = p[i]; | |
c = i; | |
} | |
} | |
return GetSlice(s2, c - len, len * 2 + 1); | |
} | |
private static IEnumerable<string> AllPalindromes(char[] s2, int[] p, int minLength, int maxLength = int.MaxValue) | |
{ | |
int s2len = s2.Length; | |
for (int i = 1; i < s2len; i++) | |
{ | |
var len = p[i]; | |
if (len >= minLength && len <= maxLength) | |
yield return GetSlice(s2, i - len, len * 2 + 1); | |
} | |
} | |
private static int[] PreProcess(char[] s2) | |
{ | |
int s2len = s2.Length; | |
int[] p = new int[s2len]; | |
int c = 0, r = 0; | |
int m = 0, n = 0; | |
for (int i = 1; i < s2len; i++) | |
{ | |
if (i > r) | |
{ | |
p[i] = 0; m = i - 1; n = i + 1; | |
} | |
else | |
{ | |
int i2 = c * 2 - i; | |
if (p[i2] < (r - i - 1)) | |
{ | |
p[i] = p[i2]; | |
m = -1; // This signals bypassing the while loop below. | |
} | |
else | |
{ | |
p[i] = r - i; | |
n = r + 1; m = i * 2 - n; | |
} | |
} | |
while (m >= 0 && n < s2len && s2[m] == s2[n]) | |
{ | |
p[i]++; m--; n++; | |
} | |
if ((i + p[i]) > r) | |
{ | |
c = i; r = i + p[i]; | |
} | |
} | |
return p; | |
} | |
private static char[] AddBoundaries(char[] cs) | |
{ | |
if (cs == null || cs.Length == 0) | |
return "||".ToCharArray(); | |
char[] cs2 = new char[cs.Length * 2 + 1]; | |
for (int i = 0; i < (cs2.Length - 1); i = i + 2) | |
{ | |
cs2[i] = '|'; | |
cs2[i + 1] = cs[i / 2]; | |
} | |
cs2[cs2.Length - 1] = '|'; | |
return cs2; | |
} | |
private static char[] RemoveBoundaries(char[] cs) | |
{ | |
if (cs == null || cs.Length < 3) | |
return "".ToCharArray(); | |
char[] cs2 = new char[(cs.Length - 1) / 2]; | |
for (int i = 0; i < cs2.Length; i++) | |
cs2[i] = cs[i * 2 + 1]; | |
return cs2; | |
} | |
private static string GetSlice(char[] s2, int start, int len) | |
{ | |
char[] res = new char[len]; | |
Array.Copy(s2, start, res, 0, len); | |
return new string(RemoveBoundaries(res)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment