题目解析: 4. 环状DNA序列整理 | 豆包MarsCode AI刷题

72 阅读3分钟

问题描述

环状 DNA 又称超螺旋,即一段碱基序列呈现环状,在分析时,需要将相同序列的环状 DNA 分到相同组内,现需将环状碱基序列按照最小表示法进行排序。

一段长度为 n 的碱基序列,按照顺时针方向,碱基序列可以从任意位置起开始该序列顺序,因此长度为 n 的碱基序列有 n 种表示法。例如:长度为 6 的碱基序列 CGAGTC,有 CGAGTCGAGTCCAGTCCG 等表示法。在这些表示法中,字典序最小的称为“最小表示”。

输入一个长度为 nn <= 100)的环状碱基序列(只包含 ACGT 这 4 种碱基)的一种表示法,输出该环状碱基序列的最小表示。

例如: ATCA 的最小表示是 AATC CGAGTC 的最小表示是 AGTCCG

输入描述

一段 DNA 碱基序列

输出描述

DNA 碱基序列的最小表示

备注n <= 100 DNA 由大写英文字母 AGCT 组成

示例 1 输入:ATCA 输出:AATC

示例 2 输入:CGAGTC 输出:AGTCCG

题解

我觉得这道题的意思是,给定一个环状的碱基序列,我们需要找到它的最小表示,也就是在所有可能的旋转中,字典序最小的那个。

我的想法是,既然环状序列可以从任意位置开始,那么我们可以生成这个序列的所有旋转版本。具体来说,就是把原始序列的每个位置都当作起点,然后生成 nn 个长度为 nn 的序列。

接下来,我们把所有生成的序列放到一起,比较它们的字典序,找出最小的那个。

举个例子,比如序列 CGAGTC,它的长度是 6,我们可以生成 6 个旋转版本:

  1. CGAGTC
  2. GAGTCC
  3. AGTCCG
  4. GTCCGA
  5. TCCGAG
  6. CCGAGT

然后比较这些序列的字典序,发现 AGTCCG 是最小的,所以它就是最小表示。

对于实现,这个过程其实并不复杂。因为 nn 的最大值是 100,所以即使生成所有的旋转序列,时间复杂度也是可以接受的。

总结一下步骤:

  1. 输入 原始序列 SS,长度为 nn。

  2. 初始化 一个最小序列 min_S=Smin_S=S。

  3. 循环 从 i=1i=1 到 n−1n−1:

    • 生成序列 TT,将 SS 循环右移 ii 位(或左移 n−in−i 位)。
    • 比较 TT 和 min_Smin_S,如果 TT 比 min_Smin_S 小,则更新 min_S=Tmin_S=T。
  4. 输出 min_Smin_S。

这样,我们就能找到环状碱基序列的最小表示了。

#include <iostream>
#include <string>

std::string solution(std::string dna_sequence) {
    int len = dna_sequence.length();
    std::string min = dna_sequence;

    dna_sequence = dna_sequence + dna_sequence;

    for(int i = 1; i < len; i ++){
        std::string tmp = dna_sequence.substr(i, len);
        if(tmp < min) min = tmp;
    }
    return min;
}

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;
}