Reconstructing Evolutionary Histories

When we calculate the Hamming distance between two genetic strings, we can easily infer a collection of point mutations that occurred on the evolutionary path between the two strings by simply examining the mismatched symbols. However, when calculating the reversal distance (see “Reversal Distance”), we only have the minimum number of reversals separating two permutations.
The computational problem of sorting by reversals demands instead that we provide a minimum list of reversals transforming one permutation into another. As a result, sorting by reversals subsumes the calculation of reversal distance: once we have a minimum collection of reversals, the reversal distance follows immediately.

Problem

A reversal of a permutation can be encoded by the two indices at the endpoints of the interval that it inverts; for example, the reversal that transforms (4,1,2,6,3,5) into (4,1,3,6,2,5) is encoded by [3,5].
A collection of reversals sorts ππ into γγ if the collection contains drev(π,γ)drev(π,γ) reversals, which when successively applied to π yield γ.
Given: Two permutations ππ and γ, each of length 10.
Return: The reversal distance drev(π,γ), followed by a collection of reversals sorting π into γ. If multiple collections of such reversals exist, you may return any one.

Sample Dataset

1 2 3 4 5 6 7 8 9 10
1 8 9 3 2 7 6 5 4 10

Sample Output

2
4 9
2 5

Solution

本题就是通过终点进行回溯操作即可。

  1. from typing import List
  2. class Solution:
  3. def backtrace(self, d: dict, end: str) -> 'List[parent, reversalRange, child]':
  4. """从终点逆推到源头,返回经过路径的所有信息,那么路径数组长度 就是反转距离"""
  5. path = []
  6. while True: # 非源头
  7. path.append(d[end])
  8. end = d[end][0]
  9. if d[end][0] == end: break
  10. return path
  11. def reversalDistance(self, s: List[int], t: List[int]) -> int:
  12. if s == t: return []
  13. s, t = tuple(s), tuple(t)
  14. # 1. 双源 BFS
  15. from collections import deque
  16. d1, d2 = {s: [s, (-1, -1), s]}, {t: [t, (-1, -1), t]} # 图节点从源节点翻转次数
  17. q1, q2 = deque([s, ]), deque([t, ]) # 两个队列一起变换
  18. while q1 or q2:
  19. for q, sign, set1, set2 in [(q1, s, d1, d2), (q2, t, d2, d1)]:
  20. for _ in range(len(q)):
  21. parent = q.popleft() # 弹出图节点
  22. # 2. 枚举当前序列所有翻转可能
  23. for start in range(len(parent)):
  24. for end in range(start + 1, len(parent)):
  25. child = parent[:start] + parent[start:end+1][::-1] + parent[end+1:]
  26. if child not in set1:
  27. set1[child] = [parent, (start, end), child] # 父亲,反转区间,儿子
  28. q.append(child)
  29. # [s->...->child] [child<-...<-t]
  30. if child in set2:
  31. # print('BFS meet at', child)
  32. if sign == t: # t 的 child 遇到 s 路径中的值
  33. set1, set2 = set2, set1
  34. s2child = self.backtrace(set1, child)[::-1] # 翻转
  35. child2t = [[end, revRange, start] for start, revRange, end in self.backtrace(set2, child)]
  36. return s2child + child2t
  37. data = """
  38. 9 3 1 4 2 6 5 10 8 7
  39. 2 3 4 6 7 1 8 5 9 10
  40. """
  41. data = [line for line in data.splitlines() if line]
  42. ss = Solution()
  43. for i in range(0, len(data), 2):
  44. s = list(map(int, data[i].split()))
  45. t = list(map(int, data[i+1].split()))
  46. ret = ss.reversalDistance(s, t)
  47. print(len(ret))
  48. for start, revRange, end in ret:
  49. # print(' '.join(f'{n:02d}' for n in start))
  50. # print(' '.join('^^' if revRange[0] <= i <= revRange[1] else ' ' for i in range(len(s))), end=' ')
  51. print(revRange[0] + 1, revRange[1] + 1)
  52. # if len(ret):
  53. # print(' '.join(f'{n:02d}' for n in end))
  54. # print('\n')