Advertisement
Guest User

Untitled

a guest
Apr 12th, 2019
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 2.82 KB | None | 0 0
  1. import itertools
  2.  
  3. #dirty hack to make slices hashable
  4. def slice(*args):
  5. if len(args) == 1:
  6. return (args[0], args[0]+1)
  7. else:
  8. return tuple(args)
  9.  
  10. def at(s, slice):
  11. i,j = slice
  12. return s[i:j]
  13.  
  14. def overlaps(a,b):
  15. #returns True if slices a and b overlap, False otherwise.
  16.  
  17. def overlaps_scalar(slice, idx):
  18. #returns True if the scalar index value falls within the boundaries of the slice.
  19. return slice[0] <= idx < slice[1]
  20.  
  21. return overlaps_scalar(a, b[0]) or overlaps_scalar(a, b[1]-1) or overlaps_scalar(b, a[0]) or overlaps_scalar(b, a[1]-1)
  22.  
  23. #locate the slices of all right-semi-palindromes in a string.
  24. #a semi-palindrome is a non-empty string that is one character short of a palindrome. Examples include "ABCDCB", "ACECAR", "GHHGF" and "XY"
  25. #a right-semi-palindrome is a semi-palindrome whose missing character is at the right end. Examples include "ABCDCB", "FGHHG", and "XY"
  26. def iter_right_semi_palindrome_slices(s):
  27. for delta in (0,1): #need to iterate twice to catch strings of odd and even length
  28. for i in range(len(s)-delta):
  29. j = i+delta
  30. yield slice(i,j+1)
  31. while i > 0 and j < len(s)-1 and s[i] == s[j+1]:
  32. i -= 1
  33. j += 1
  34. yield slice(i, j+1)
  35.  
  36. def iter_left_semi_palindrome_slices(s):
  37. s = s[::-1]
  38. for i,j in iter_right_semi_palindrome_slices(s):
  39. yield slice(len(s) - j, len(s) - i)
  40.  
  41. def find_palindrome_slices(s):
  42. d = {}
  43. for idx, c in enumerate(s):
  44. d.setdefault(c, []).append(slice(idx))
  45.  
  46. pairs = set()
  47. #base case 1: len(p) == 1 and q is a left-semi-palindrome.
  48. for q in iter_left_semi_palindrome_slices(s):
  49. char = s[q[1]-1]
  50. for p in d[char]:
  51. if not overlaps(q, p):
  52. pairs.add((p,q))
  53.  
  54. #base case 2: len(q) == 1 and p is a right-semi-palindrome.
  55. for p in iter_right_semi_palindrome_slices(s):
  56. char = s[p[0]]
  57. for q in d[char]:
  58. if not overlaps(p,q):
  59. pairs.add((p,q))
  60.  
  61. inductive_pairs = set()
  62. #inductive case. find p' and q', which are p and q but with one additional character on the left or right respectively.
  63. for p,q in pairs:
  64. while True:
  65. p = slice(p[0]-1, p[1])
  66. q = slice(q[0], q[1]+1)
  67. if p[0] < 0 or q[1] > len(s): break
  68. if overlaps(p,q): break
  69. if s[p[0]] != s[q[1]-1]: break
  70. inductive_pairs.add((p,q))
  71.  
  72. return pairs | inductive_pairs
  73.  
  74. s = "ABBA"
  75. for p,q in find_palindrome_slices(s):
  76. print("=======================")
  77. print(" " * p[0] + "v"*(p[1] - p[0]))
  78. print(s)
  79. print(" " * q[0] + "^"*(q[1] - q[0]))
  80.  
  81. p = s[p[0]:p[1]]
  82. q = s[q[0]:q[1]]
  83. print(f"{p} + {q} == {p+q}")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement