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

48 阅读4分钟

问题描述

小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'

具体思路

要解决这个问题,我们需要找到给定环状 DNA 序列的最小字典序表示。一个有效的方法是将序列自身拼接一次,然后在这个双倍长度的字符串中寻找长度为 n 的子串,这个子串就是原序列的所有可能表示中的字典序最小的那个。

具体步骤如下:

  1. 拼接字符串:首先,我们将输入的 DNA 序列 dna_sequence 拼接两次,得到一个新的字符串 double_sequence。这样做的目的是模拟环状结构,使得可以从任何位置开始读取序列。
  2. 初始化最小序列:我们假设第一个长度为 n 的子串是最小的,并将其存储在 min_sequence 中。
  3. 遍历查找最小字典序子串:我们从第二个字符开始遍历 double_sequence,每次提取长度为 n 的子串,并与当前的 min_sequence 进行比较。如果新的子串更小,则更新 min_sequence
  4. 返回结果:最后返回找到的最小字典序子串。

代码

#include <iostream>
#include <string>
std::string solution(std::string dna_sequence) {
    int n = dna_sequence.length();
    std::string double_sequence = dna_sequence + dna_sequence;
    std::string min_sequence = double_sequence.substr(0, n);
    for (int i = 1; i < n; ++i) {
        std::string current_sequence = double_sequence.substr(i, n);
        if (current_sequence < min_sequence) {
            min_sequence = current_sequence;
        }
    }
    return min_sequence;
}
int main() {
    // You can add more test cases here
    std::cout << (solution("ATCA") == "AATC") << std::endl;
    std::cout << (solution("CGAGTC") == "AGTCCG") << std::endl;
    std::cout << (solution("TCATGGAGTGCTCCTGGAGGCTGAGTCCATCTCCAGTAG") == "AGGCTGAGTCCATCTCCAGTAGTCATGGAGTGCTCCTGG") << std::endl;
    return 0;
}

解释:

  1. 拼接字符串:首先,我们将输入的 DNA 序列 dna_sequence 拼接两次,形成 double_sequence。这样做的目的是模拟环状结构,使得可以从任何位置开始读取序列。

  2. 初始化最小序列:我们假设第一个长度为 n 的子串是最小的,并将其存储在 min_sequence 中。

  3. 遍历查找最小字典序子串:我们从第二个字符开始遍历 double_sequence,每次提取长度为 n 的子串,并与当前的 min_sequence 进行比较。如果新的子串更小,则更新 min_sequence

  4. 返回结果:最后返回找到的最小字典序子串。

总结

1. 理解问题的本质

环状结构的特性:DNA 序列是环状的,这意味着可以从任何位置开始读取序列。因此,我们需要找到所有可能的表示方式中的字典序最小的那个。 拼接字符串的方法:通过将序列自身拼接一次,我们可以模拟环状结构,从而简化问题的处理。

2. 算法设计的重要性

遍历与比较:通过遍历双倍长度的字符串并比较子串,我们能够有效地找到字典序最小的子串。这种方法的时间复杂度为 O(n^2),对于较短的 DNA 序列来说是可以接受的。 优化空间:虽然直接拼接字符串增加了一些空间开销,但这种开销是可控的,并且大大简化了逻辑实现。

3. 编码实践

字符串操作:熟悉 C++ 中字符串的操作方法,如 substr,对于解决此类问题非常有帮助。 边界条件:注意处理各种边界条件,例如空字符串或极短的字符串,确保代码的健壮性。

4. 测试与验证

多测试用例:编写多个测试用例来验证代码的正确性,包括普通情况和边界情况。 调试技巧:学会使用调试工具和方法,快速定位和修复代码中的错误。

5. 性能考虑

时间复杂度:虽然当前的解决方案在大多数情况下是有效的,但对于非常大的输入数据,可能需要进一步优化算法以提高效率。

空间复杂度:在保证功能正确的前提下,尽量优化空间使用,避免不必要的内存消耗。