青训营笔记 | 豆包MarsCode AI刷题

107 阅读38分钟

问题描述

小C正在研究一种环状的 DNA 结构,它由四种碱基ACGT构成。这种环状结构的特点是可以从任何位置开始读取序列,因此一个长度为 n 的碱基序列可以有 n 种不同的表示方式。小C的任务是从这些表示中找到字典序最小的序列,即该序列的“最小表示”。

例如:碱基序列 ATCA 从不同位置读取可能的表示有 ATCA, TCAA, CAAT, AATC,其中 AATC 是字典序最小的表示。

测试样例

样例1:

输入:dna_sequence = "ATCA"
输出:'AATC'

样例2:

输入:dna_sequence = "CGAGTC"
输出:'AGTCCG'

样例3:

输入:dna_sequence = "TTGAC"
输出:'ACTTG'

方法解析

环状 DNA 的特点是从任何位置开始读取都能生成一个新的序列。因此,直接暴力生成所有表示并排序会非常耗时。而双倍字符串法利用了循环结构的特点,高效找到字典序最小的起始点。

详细步骤

1. 双倍字符串的生成

将 DNA 序列拼接成两个相同序列的连续字符串(即 dna_sequence + dna_sequence)。

这样可以模拟环状结构的所有表示,而无需实际处理循环。

将 DNA 序列拼接到自身后,比如序列 ATCA 变成 ATCAATCA

  •  拼接后的字符串可以直接模拟环状读取,因为从任何起点开始读取 n 长度子串,都可以被视为环状的一个表示。
2. 初始化最小位置

设置 min_start = 0,表示当前最小字典序子串的起始位置。

3. 遍历每个起点

从 1 到 n-1 遍历每个可能的起始点,生成长度为 n 的子串,并与当前最小子串进行比较:

  • 如果新子串的字典序小于当前最小子串,则更新 min_start
  1. 字典序比较

    • 在双倍字符串中,依次找到每个可能的子串,并比较字典序。
    • 保留字典序最小的子串,即为答案。
  2. 输出结果

    • 返回找到的最小子串作为 DNA 序列的最小表示。

Python 实现

时间复杂度

  • 遍历每个起点: O(n) 次,因为在最坏情况下,需要比较 n 次,每次比较的时间为 O(n)
    • 总时间复杂度为 O(n^2)
  • 空间复杂度: 双倍字符串占用 O(2n),最终结果为 O(n)