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

49 阅读2分钟

思路

  1. 构造新的字符串:为了模拟环状结构,我们将原字符串 s 拼接两次,形成一个新的字符串 s + s。这样,所有可能的子串都可以在新的字符串中找到。
  2. 找到最小表示的起始位置:遍历所有可能的起始位置(从 0 到 n-1),找到字典序最小的子串的起始位置。
  3. 返回最小表示:从找到的起始位置截取长度为 n 的子串,即为最小表示。

代码实现

Python
深色版本
def solution(dna_sequence):
    def compute_kmp_table(s):
        n = len(s)
        kmp = [0] * n
        j = 0
        for i in range(1, n):
            while j > 0 and s[i] != s[j]:
                j = kmp[j - 1]
            if s[i] == s[j]:
                j += 1
            kmp[i] = j
        return kmp

    n = len(dna_sequence)
    concatenated = dna_sequence + dna_sequence
    kmp = compute_kmp_table(concatenated)
    
    min_index = 0
    for i in range(1, n):
        if concatenated[i:i+n] < concatenated[min_index:min_index+n]:
            min_index = i
    
    return concatenated[min_index:min_index+n]

if __name__ == "__main__":
    print(solution("ATCA") == "AATC")
    print(solution("CGAGTC") == "AGTCCG")
    print(solution("TCATGGAGTGCTCCTGGAGGCTGAGTCCATCTCCAGTAG") == "AGGCTGAGTCCATCTCCAGTAGTCATGGAGTGCTCCTGG")

代码详解

  1. compute_kmp_table

    • 这个函数用于计算 KMP 算法中的 next 数组。虽然在这个问题中我们没有直接使用 next 数组来匹配字符串,但它帮助我们在构造的 s + s 字符串中找到最小表示的起始位置。
    • kmp[i] 表示前缀 s[0:i+1] 的最长公共前后缀的长度。
    • 通过双指针 i 和 j 来填充 kmp 数组。
  2. 构造新的字符串

    • concatenated = dna_sequence + dna_sequence:将原字符串拼接两次,形成一个新的字符串 s + s,这样可以模拟环状结构。
  3. 找到最小表示的起始位置

    • 初始化 min_index 为 0。
    • 遍历所有可能的起始位置(从 1 到 n-1),比较每个子串 concatenated[i:i+n] 和当前最小的子串 concatenated[min_index:min_index+n]
    • 如果当前子串的字典序更小,更新 min_index
  4. 返回最小表示

    • 从找到的起始位置 min_index 截取长度为 n 的子串,即为最小表示。