Rearrangements Power Large-Scale Genomic Changes
Perhaps the most common type of genome rearrangement is an inversion, which flips an entire interval of DNA found on the same chromosome. As in the case of calculating Hamming distance (see “Counting Point Mutations”), we would like to determine the minimum number of inversions that have occurred on the evolutionary path between two chromosomes. To do so, we will use the model introduced in “Enumerating Gene Orders” in which a chromosome is represented by a permutation of its synteny blocks.
Problem
A reversal of a permutation creates a new permutation by inverting some interval of the permutation; (5,2,3,1,4)(5,2,3,1,4), (5,3,4,1,2)(5,3,4,1,2), and (4,1,2,3,5)(4,1,2,3,5) are all reversals of (5,3,2,1,4)(5,3,2,1,4). The reversal distance between two permutations ππ and σσ, written drev(π,σ)drev(π,σ), is the minimum number of reversals required to transform π into σ (this assumes that ππ and σσ have the same length).
Given: A collection of at most 5 pairs of permutations, all of which have length 10.
Return: The reversal distance between each permutation pair.
Sample Dataset
1 2 3 4 5 6 7 8 9 103 1 5 2 7 4 9 6 10 83 10 8 2 5 4 7 1 6 95 2 3 1 7 4 10 8 6 98 6 7 9 4 1 3 10 2 58 2 7 6 9 1 5 3 10 43 9 10 4 1 8 6 7 5 22 9 8 5 1 7 3 4 6 101 2 3 4 5 6 7 8 9 101 2 3 4 5 6 7 8 9 10
Sample Output
9 4 5 7 0
Hint
Don’t be afraid to try an ugly solution.
贪心错误!
题意规定 A reversal of a permutation 意思是某个全排列区间 [i, j] 的所有数字发生翻转。然后 reversal distance 定义是一个全排列 π 通过连续进行 n 次区间 [i, j] (可重叠)翻转获得另一个全排列 σ 的次数。
3 10 8 2 5 4 7 1 6 95 2 3 1 7 4 10 8 6 93 10 8 2 5 4 7 1 6 95 2 3 1 7 4 10 8 6 9|---| 将末尾 3 反转到头部3 10 8 2 5 4 7 1 6 93 2 5 1 7 4 10 8 6 9|---------| 将末尾 10 反转到头部3 10 8 2 5 4 7 1 6 93 10 4 7 1 5 2 8 6 9|---------| 将末尾 8 反转到头部3 10 8 2 5 4 7 1 6 93 10 8 2 5 1 7 4 6 9|---| 将末尾 4 反转到头部3 10 8 2 5 4 7 1 6 93 10 8 2 5 4 7 1 6 9
因此暴力的方法就是假设区间 内都有
成立,再依次检测
是否相等,不相等则在
中找到
。由于题目给定全排列不包含重复数字且有答案,因此
是唯一存在的!因此将
进行翻转使得
。
from typing import Listclass Solution:def reversalDistance(self, s: List[int], t: List[int]) -> int:distance = 0for i in range(len(s)):if s[i] != t[i]:distance += 1for j in range(i + 1, len(t)):if s[i] == t[j]:t[i:j+1] = reversed(t[i:j+1])breakreturn distancedata = """8 6 7 9 4 1 3 10 2 58 2 7 6 9 1 5 3 10 4"""data = [line for line in data.splitlines() if line]# print(data)ss = Solution()for i in range(0, len(data), 2):s = list(map(int, data[i].split()))t = list(map(int, data[i+1].split()))print(s, t)print(ss.reversalDistance(s, t))
但是上面输出 7 是错误的!
s : [8, 6, 7, 9, 4, 1, 3, 10, 2, 5]t1: [8, 2, 7, 6, 9, 1, 5, 3, 10, 4]|-----|t2: [8, 6, 7, 2, 9, 1, 5, 3, 10, 4]s : [8, 6, 7, 9, 4, 1, 3, 10, 2, 5]t1: [8, 6, 7, 2, 9, 1, 5, 3, 10, 4]|--|t2: [8, 6, 7, 9, 2, 1, 5, 3, 10, 4]s : [8, 6, 7, 9, 4, 1, 3, 10, 2, 5]t1: [8, 6, 7, 9, 2, 1, 5, 3, 10, 4]|---------------|t2: [8, 6, 7, 9, 4, 10, 3, 5, 1, 2]s : [8, 6, 7, 9, 4, 1, 3, 10, 2, 5]t1: [8, 6, 7, 9, 4, 10, 3, 5, 1, 2]|---------|t2: [8, 6, 7, 9, 4, 1, 5, 3, 10, 2]s : [8, 6, 7, 9, 4, 1, 3, 10, 2, 5]t1: [8, 6, 7, 9, 4, 1, 5, 3, 10, 2]|--|t2: [8, 6, 7, 9, 4, 1, 3, 5, 10, 2]s : [8, 6, 7, 9, 4, 1, 3, 10, 2, 5]t1: [8, 6, 7, 9, 4, 1, 3, 5, 10, 2]|---|t2: [8, 6, 7, 9, 4, 1, 3, 10, 5, 2]s : [8, 6, 7, 9, 4, 1, 3, 10, 2, 5]t1: [8, 6, 7, 9, 4, 1, 3, 10, 5, 2]|--|t2: [8, 6, 7, 9, 4, 1, 3, 10, 2, 5]
单源 BFS
from typing import Listclass Solution:def reversalDistance(self, s: List[int], t: List[int]) -> int:if s == t: return 0s, t = tuple(s), tuple(t)# 1. 单源 BFSfrom collections import dequedistance = 0visited = {s, } # 图节点从源节点翻转次数q = deque([s, ]) # 两个队列一起变换while q:distance += 1 # 进队列就多义词转换for _ in range(len(q)):s = q.popleft() # 弹出图节点# 2. 枚举当前序列所有翻转可能for start in range(len(s)):for end in range(start + 1, len(s)):ss = s[:start] + s[start:end+1][::-1] + s[end+1:]if ss == t:return distanceif ss not in visited:visited.add(ss)q.append(ss)return -1data = """1 2 3 4 5 6 7 8 9 103 1 5 2 7 4 9 6 10 83 10 8 2 5 4 7 1 6 95 2 3 1 7 4 10 8 6 98 6 7 9 4 1 3 10 2 58 2 7 6 9 1 5 3 10 43 9 10 4 1 8 6 7 5 22 9 8 5 1 7 3 4 6 101 2 3 4 5 6 7 8 9 101 2 3 4 5 6 7 8 9 10"""data = [line for line in data.splitlines() if line]ss = Solution()for i in range(0, len(data), 2):s = list(map(int, data[i].split()))t = list(map(int, data[i+1].split()))print(s, t)print(ss.reversalDistance(s, t))
双源 BFS
from typing import Listclass Solution:def reversalDistance(self, s: List[int], t: List[int]) -> int:if s == t: return 0s, t = tuple(s), tuple(t)# 1. 双源 BFSfrom collections import dequedistance = 0d1, d2 = {s: 0}, {t: 0} # 图节点从源节点翻转次数q1, q2 = deque([s, ]), deque([t, ]) # 两个队列一起变换while q1 or q2:distance += 1 # 进队列就多义词转换for _ in range(len(q1)):s = q1.popleft() # 弹出图节点# 2. 枚举当前序列所有翻转可能for start in range(len(s)):for end in range(start + 1, len(s)):ss = s[:start] + s[start:end+1][::-1] + s[end+1:]if ss in d2:return distance + d2[ss]if ss not in d1:d1[ss] = distanceq1.append(ss)for _ in range(len(q2)):t = q2.popleft() # 弹出图节点# 2. 枚举当前序列所有翻转可能for start in range(len(t)):for end in range(start + 1, len(t)):tt = t[:start] + t[start:end+1][::-1] + t[end+1:]if tt in d1:return distance + d1[tt]if tt not in d2:d2[tt] = distanceq2.append(tt)return -1data = """1 2 3 4 5 6 7 8 9 103 1 5 2 7 4 9 6 10 83 10 8 2 5 4 7 1 6 95 2 3 1 7 4 10 8 6 98 6 7 9 4 1 3 10 2 58 2 7 6 9 1 5 3 10 43 9 10 4 1 8 6 7 5 22 9 8 5 1 7 3 4 6 101 2 3 4 5 6 7 8 9 101 2 3 4 5 6 7 8 9 10"""data = [line for line in data.splitlines() if line]ss = Solution()for i in range(0, len(data), 2):s = list(map(int, data[i].split()))t = list(map(int, data[i+1].split()))print(s, t)print(ss.reversalDistance(s, t))
速度对比:
b07@SB:~$ time python ./test.py[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [3, 1, 5, 2, 7, 4, 9, 6, 10, 8]9[3, 10, 8, 2, 5, 4, 7, 1, 6, 9] [5, 2, 3, 1, 7, 4, 10, 8, 6, 9]4[8, 6, 7, 9, 4, 1, 3, 10, 2, 5] [8, 2, 7, 6, 9, 1, 5, 3, 10, 4]5[3, 9, 10, 4, 1, 8, 6, 7, 5, 2] [2, 9, 8, 5, 1, 7, 3, 4, 6, 10]7[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]0real 3m7.231suser 3m6.420ssys 0m0.810sb07@SB:~$ time python ./test.py[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [3, 1, 5, 2, 7, 4, 9, 6, 10, 8]9[3, 10, 8, 2, 5, 4, 7, 1, 6, 9] [5, 2, 3, 1, 7, 4, 10, 8, 6, 9]4[8, 6, 7, 9, 4, 1, 3, 10, 2, 5] [8, 2, 7, 6, 9, 1, 5, 3, 10, 4]5[3, 9, 10, 4, 1, 8, 6, 7, 5, 2] [2, 9, 8, 5, 1, 7, 3, 4, 6, 10]7[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]0real 0m2.132suser 0m2.072ssys 0m0.060s
路径求解
我们知道上面翻转次数,但是如何求解最优路径呢?
901 02 03 04 05 06 07 08 09 10^^ ^^ 0 102 01 03 04 05 06 07 08 09 10^^ ^^ ^^ 0 203 01 02 04 05 06 07 08 09 10^^ ^^ 2 303 01 04 02 05 06 07 08 09 10^^ ^^ ^^ 2 403 01 05 02 04 06 07 08 09 10^^ ^^ 4 503 01 05 02 06 04 07 08 09 10^^ ^^ ^^ 7 903 01 05 02 06 04 07 10 09 08^^ ^^ ^^ 5 703 01 05 02 06 10 07 04 09 08^^ ^^ ^^ ^^ 5 803 01 05 02 06 09 04 07 10 08^^ ^^ ^^ ^^ 4 703 01 05 02 07 04 09 06 10 08403 10 08 02 05 04 07 01 06 09^^ ^^ ^^ ^^ 1 403 05 02 08 10 04 07 01 06 09^^ ^^ ^^ ^^ ^^ 3 703 05 02 01 07 04 10 08 06 09^^ ^^ ^^ 0 202 05 03 01 07 04 10 08 06 09^^ ^^ 0 105 02 03 01 07 04 10 08 06 09508 06 07 09 04 01 03 10 02 05^^ ^^ ^^ ^^ ^^ ^^ 3 808 06 07 02 10 03 01 04 09 05^^ ^^ ^^ 1 308 02 07 06 10 03 01 04 09 05^^ ^^ ^^ 7 908 02 07 06 10 03 01 05 09 04^^ ^^ 6 708 02 07 06 10 03 05 01 09 04^^ ^^ ^^ ^^ ^^ 4 808 02 07 06 09 01 05 03 10 04703 09 10 04 01 08 06 07 05 02^^ ^^ ^^ 0 210 09 03 04 01 08 06 07 05 02^^ ^^ ^^ ^^ ^^ ^^ 0 508 01 04 03 09 10 06 07 05 02^^ ^^ ^^ ^^ ^^ ^^ ^^ 1 708 07 06 10 09 03 04 01 05 02^^ ^^ ^^ ^^ 1 408 09 10 06 07 03 04 01 05 02^^ ^^ ^^ 4 608 09 10 06 04 03 07 01 05 02^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ 2 908 09 02 05 01 07 03 04 06 10^^ ^^ ^^ 0 202 09 08 05 01 07 03 04 06 100
请见下一个题目
