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

49 阅读3分钟

题目链接

代码练习 - MarsCode

题目描述

问题描述

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

题目思路

要解决这道题,我们首先要了解什么是字典序

字典序(Lexicographic Order)是一种用于比较字符串或序列的顺序,类似于字典中单词的排列方式。在字典序中,字符串的比较是基于字符的顺序,从左到右逐个字符进行比较。以下是字典序的几个关键点:

字典序的定义

  1. 基本原则:

    • 字符串的比较从第一个字符开始,逐个比较字符的 ASCII 值(或 Unicode 值)。
    • 如果第一个字符相同,则继续比较下一个字符,直到找到不同的字符。
    • 如果一个字符串是另一个字符串的前缀,则较短的字符串被认为是较小的。例如,"apple" < "applepie"。
  2. 字符顺序:

    • 在 ASCII 编码中,字符的顺序是:空格 < '0' < '1' < ... < '9' < 'A' < 'B' < ... < 'Z' < 'a' < 'b' < ... < 'z'。这意味着大写字母的排序在小写字母之前。

怎么比较字符串呢

OK,当我们学会了什么是字典序之后,就有一个问题了,我们该如何比较两个字符串的字典序?

实际上,牛逼轰轰的c++内置了比较功能,我们只需要用 <>和==来判断就可以了

怎么解决环问题呢

题目中说,dna是一个环,我们可以通过添加断点的位置来决定这个环开始的位置,那么如何构建一个环组成的字符串就成了重中之重

巧妙的来了

我们可以将这个字符串和它本身进行拼接,从而延长一倍,这样我们可以在前半段选取断点,然后向后截取长度为一半的子串,这样就完成了环的截断问题。

接下来就很简单了,我们只需要遍历所有子串,选择最大的那个就好

题解代码

#include <iostream>
#include <string>std::string solution(std::string dna_sequence) {
    // 初始化结果字符串
    std::string result = dna_sequence;
    // 获取字符串长度
    int n = dna_sequence.length();
    // 首先将字符串扩大两倍,以处理环形结构
    dna_sequence += dna_sequence;
    // 遍历字符串的每个位置
    for (int i = 0; i < n; i++) {
        // 如果当前位置的子串比之前的子串字典序小,更新结果字符串
        if(result > dna_sequence.substr(i, n))result = dna_sequence.substr(i, n);
    }
    // 返回结果字符串
    return result;
}
​
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;
}

反思

本题的难点就是如何处理环装字符串的断点问题