环状 DNA 序列的最小表示法python暴力解题 | 豆包MarsCode AI刷题

78 阅读3分钟

环状 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. 最小表示的算法:

  • 想法:我们从字符串的每个位置开始读取,并将所有的旋转存入一个数组,然后通过比较字典序找到最小的一个。

  • 具体步骤:

    1. 构建循环字符串:将DNA序列自身连接起来形成一个新的字符串 s + s​,这个字符串包含了所有的旋转表示。
    2. 通过比较找到最小的旋转:从 0​ 到 n-1​ 的所有位置进行比较,找到字典序最小的旋转。
  • 具体实现:

    1. 先构造一个字符串 s + s​,这样可以方便地获取所有的旋转。
    2. 使用一种滑动窗口的方法来遍历得到所有可能的旋转
    3. 然后逐一比较字典序。
    4. 选取字典序最小的旋转。
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)​。