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

50 阅读2分钟

问题描述

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

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


测试样例

样例1:

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

样例2:

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

样例3:

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

def solution(dna_sequence):
    # Please write your code here
    s = dna_sequence
    length = len(s)
    s = s + s
    f = [-1] * 2 * length
    k = 0

    for j in range(1, 2 * length):
        i = f[j-k-1]
        while i != -1 and s[j] != s[k + i + 1]:
            if s[j] < s[k + i +1]:
                k = j - i - 1
            i = f[i]
        if s[j] != s[k + i +1]:
            if s[j] < s[k]:
                k = j
            f[j - k] = -1
        else:
            f[j - k] = i + 1

    return s[k:k + length]

通过扩展原始DNA序列来找到最小表示。具体来说,将原始序列 s 复制一次,形成一个长度为 2 * length 的新序列 s + s。然后,使用一个数组 f 来记录前缀匹配的信息,并通过比较不同起点的子序列来找到字典序最小的表示。 以下是思路解释:

思路解释

  1. 扩展序列

    • 将原始序列 s 复制一次,形成 s + s。这样做的目的是为了模拟环状结构,使得可以从任何位置开始读取序列。
  2. 前缀匹配数组 f

    • 使用一个数组 f 来记录前缀匹配的信息。f[i] 表示在位置 i 之前的最长前缀匹配长度。
  3. 比较和更新

    • 通过遍历扩展后的序列,比较不同起点的子序列,并更新 k 的值,使得 k 始终指向当前找到的最小表示的起始位置。
  4. 返回结果

    • 最后,从 k 位置开始,截取长度为 length 的子序列作为结果返回。

代码中的关键步骤

  • 扩展序列s = s + s
  • 初始化前缀匹配数组f = [-1] * 2 * length
  • 遍历扩展后的序列for j in range(1, 2 * length)
  • 比较和更新 k:通过 i = f[j-k-1]s[j] != s[k + i + 1] 进行比较和更新。