|
| 1 | +''' |
| 2 | +Author: OMKAR PATHAK |
| 3 | +Created at: 27th August 2017 |
| 4 | +''' |
| 5 | + |
| 6 | + |
| 7 | +def manacher(string): |
| 8 | + """ |
| 9 | + Computes length of the longest palindromic substring centered on each char |
| 10 | + in the given string. The idea behind this algorithm is to reuse previously |
| 11 | + computed values whenever possible (palindromes are symmetric). |
| 12 | +
|
| 13 | + Example (interleaved string): |
| 14 | + i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
| 15 | + s # a # b # c # q # q # q # q # q # q # x # y # |
| 16 | + P 0 1 0 1 0 1 0 1 2 3 4 5 6 5 4 ? |
| 17 | + ^ ^ ^ ^ |
| 18 | + mirror center current right |
| 19 | +
|
| 20 | + We're at index 15 wondering shall we compute (costly) or reuse. The mirror |
| 21 | + value for 15 is 9 (center is in 12). P[mirror] = 3 which means a palindrome |
| 22 | + of length 3 is centered at this index. A palindrome of same length would be |
| 23 | + placed in index 15, if 15 + 3 <= 18 (right border of large parlindrome |
| 24 | + centered in 12). This condition is satisfied, so we can reuse value from |
| 25 | + index 9 and avoid costly computation. |
| 26 | + """ |
| 27 | + if type(string) is not str: |
| 28 | + raise TypeError("Manacher Algorithm only excepts strings, not {}".format(str(type(string)))) |
| 29 | + |
| 30 | + # Get the interleaved version of a given string. 'aaa' --> '^#a#a#a#$'. |
| 31 | + # Thanks to this we don't have to deal with even/odd palindrome |
| 32 | + # length problem. |
| 33 | + string_with_bounds = '#'.join('^{}$'.format(string)) |
| 34 | + length = len(string_with_bounds) |
| 35 | + P = [0] * length |
| 36 | + center = right = 0 |
| 37 | + |
| 38 | + for i in range(1, length - 1): |
| 39 | + P[i] = (right > i) and min(right - i, P[2 * center - i]) |
| 40 | + |
| 41 | + # Attempt to expand palindrome centered at i |
| 42 | + while string_with_bounds[i + 1 + P[i]] == string_with_bounds[i - 1 - P[i]]: |
| 43 | + P[i] += 1 |
| 44 | + |
| 45 | + # If palindrome centered at i expand past R, |
| 46 | + # adjust center based on expanded palindrome. |
| 47 | + if i + P[i] > right: |
| 48 | + center, right = i, i + P[i] |
| 49 | + |
| 50 | + # Find the maximum element in P and return the string |
| 51 | + maxLen, centerIndex = max((n, i) for i, n in enumerate(P)) |
| 52 | + return string[(centerIndex - maxLen)//2: (centerIndex + maxLen)//2] |
| 53 | + |
| 54 | +def get_code(): |
| 55 | + """ |
| 56 | + returns the code for the manacher's algorithm |
| 57 | + :return: source code |
| 58 | + """ |
| 59 | + return inspect.getsource(manacher) |
0 commit comments