环状 DNA 序列的最小表示法
问题描述
小C正在研究一种环状的 DNA 结构,它由四种碱基A、C、G、T构成。这种环状结构的特点是可以从任何位置开始读取序列,因此一个长度为 n 的碱基序列可以有 n 种不同的表示方式。小C的任务是从这些表示中找到字典序最小的序列,即该序列的“最小表示”。
例如:碱基序列 ATCA 从不同位置读取可能的表示有 ATCA, TCAA, CAAT, AATC,其中 AATC 是字典序最小的表示。
测试样例
样例1:
输入:dna_sequence = "ATCA"
输出:'AATC'
样例2:
输入:dna_sequence = "CGAGTC"
输出:'AGTCCG'
样例3:
输入:dna_sequence = "TTGAC"
输出:'ACTTG'
思路
1. 环状DNA序列的特点
- 给定一个DNA序列,序列可以从任何一个位置开始读取,即这个DNA序列是环状的。
- 对于一个长度为 n 的DNA序列,它有 n 种不同的表示方式,分别是从每个位置开始向后的子串。
2. 找到字典序最小的表示
- 字典序最小的表示就是我们需要找的“最小表示”。
- 对于所有的 n 种表示,我们需要通过比较这些表示的字典序,找到最小的那个。
3. 方法:
- 暴力方法:生成所有可能的表示,逐个比较它们的字典序,每个表示需要比较
n次,找出最小的。这种方法的时间复杂度是O(n^2),因为我们需要生成n 种表示,每种表示的比较过程是O(n)。 - 优化方法:我们可以使用一种基于字符串比较的算法,称为 最小表示算法,来高效地找到字典序最小的表示。该算法的时间复杂度是
O(n),其核心思想是利用字符串的循环性质,寻找最小的“旋转”。
4. 最小表示的算法:
-
想法:我们从字符串的每个位置开始读取,并将所有的旋转存入一个数组,然后通过比较字典序找到最小的一个。
-
具体步骤:
- 构建循环字符串:将DNA序列自身连接起来形成一个新的字符串
s + s,这个字符串包含了所有的旋转表示。 - 通过比较找到最小的旋转:从
0 到n-1 的所有位置进行比较,找到字典序最小的旋转。
- 构建循环字符串:将DNA序列自身连接起来形成一个新的字符串
-
具体实现:
- 先构造一个字符串
s + s,这样可以方便地获取所有的旋转。 - 使用一种滑动窗口的方法来遍历得到所有可能的旋转
- 然后逐一比较字典序。
- 选取字典序最小的旋转。
- 先构造一个字符串
def solution(dna_sequence):
n = len(dna_sequence)
ls = [] # 用于存放可能的表示情况
s = dna_sequence + dna_sequence
for i in range(n):
# 循环遍历序列,用字符串拼接的方法来得到序列
ls.append(s[i:i+n])
min_representation = ls[0]
for rep in ls:
if rep < min_representation: # 比较序列的字典序,找到字典序最小的那个序列
min_representation = rep
return min_representation
6. 时间复杂度分析
- 假设 DNA 序列的长度是
n,构造字符串s + s的时间复杂度为O(n). - 然后比较所有旋转所需的时间复杂度是
O(n),因为每次比较两个旋转时,最多需要1次比较。 - 所以最终的时间复杂度是
O(n)。