青训营X豆包MarsCode技术训练营学习体验|豆包MarsCode AI 刷题

61 阅读6分钟

1.问题

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

解题思路:

环状 DNA 序列可以从任意位置开始读取,因此一个长度为 n 的序列可以有 n 种不同的表示方式,需要找到这些表示方式中字典序最小的那个。 由于需要比较字符串的字典序,可以使用字符串比较操作。 为了方便处理环状序列,可以将原始序列复制一份拼接在其后面,这样就可以通过简单的字符串切片来模拟所有可能的表示方式。

算法步骤

将原始序列 dna_sequence 复制一份并拼接在其后面,得到一个新的字符串 extended_sequence。 遍历 extended_sequence 中的每一个长度为 n 的子串,找到字典序最小的那个子串。 返回该子串作为结果。

具体步骤:

拼接序列String extended_sequence = dna_sequence + dna_sequence; 将原始序列复制并拼接在其后面,形成一个长度为 2n 的字符串。 遍历子串: 通过 for (int i = 1; i < n; i++) 遍历 extended_sequence 中的每一个长度为 n 的子串。 String current_sequence = extended_sequence.substring(i, i + n); 获取当前子串。 比较字典序: 使用 compareTo 方法比较当前子串与当前最小子串的字典序。 如果当前子串的字典序更小,则更新 min_sequence返回结果: 最终返回 min_sequence 作为结果。

代码:

public class Main {
public static String solution(String dna_sequence) {
    int n = dna_sequence.length();
    String extended_sequence = dna_sequence + dna_sequence; // 复制并拼接
    String min_sequence = extended_sequence.substring(0, n);

    for (int i = 1; i < n; i++) {
        String current_sequence = extended_sequence.substring(i, i + n);
        if (current_sequence.compareTo(min_sequence) < 0) {
            min_sequence = current_sequence;
        }
    }

    return min_sequence;
}

public static void main(String[] args) {
    System.out.println(solution("ATCA").equals("AATC"));//测试
    System.out.println(solution("CGAGTC").equals("AGTCCG"));//测试
    System.out.println(solution("TTGAC").equals("ACTTG"));//测试
}

}

感想:

通过这种方式,可以有效地找到环状 DNA 序列的最小表示法。解决这个问题让我深刻体会到字符串操作和环状结构处理的巧妙结合。通过将原始序列复制并拼接,我们可以轻松地模拟环状序列的所有可能表示方式,从而简化了问题的复杂性。这种技巧不仅提高了代码的可读性,还使得算法的时间复杂度保持在可接受的范围内。 在实现过程中,我学会了如何利用字符串的比较操作来找到字典序最小的子串,并通过遍历所有可能的子串来确保找到全局最优解。这种思路可以推广到其他类似的问题中,例如寻找环状数组中的最小值或最大值。 总之,这个问题的解决不仅提升了我的编程技巧,还让我对字符串处理和环状结构有了更深入的理解。通过不断练习和思考,我相信在面对类似问题时能够更加从容应对。

2.问题

问题描述

小M有一个独特的方式来收拾家中的盘子。每次用餐后,他会将盘子按照他们的序号顺序叠放。盘子的序号都是唯一的整数,并且在收拾前就是递增的。小M的叠放规则是,每一堆盘子的序号都是连续递增的,并且至少包含3个盘子。需要编写程序帮助小M确定盘子的叠放方式。

例如,输入的盘子序号是 [-3, -2, -1, 2, 10, 15, 16, 18, 19, 20],按照小M的规则,连续递增序列 -3, -2, -1 可以叠在一起表示为 -3--1,而 18, 19, 20 可以叠在一起表示为 18-20。不满足连续递增至少3个的,如 2, 10, 15, 16 都应单独列出。

解题思路:

输入是一个整数数组 plates,表示盘子的序号,这些序号是唯一的且已经按递增顺序排列。 需要将这些盘子按照连续递增的规则进行分组,每组至少包含3个盘子。 如果一组盘子不满足至少3个连续递增的条件,则单独列出。

使用一个列表来存储当前正在处理的连续递增序列。 使用一个字符串列表来存储最终的结果。

算法步骤:

遍历 plates 数组。 对于每个盘子,检查它是否可以加入当前的连续递增序列。 如果可以加入,则加入当前序列;如果不可以,则将当前序列(如果长度大于等于3)加入结果列表,并开始一个新的序列。 遍历结束后,处理最后一个序列。 将结果列表中的内容格式化为字符串并返回。

具体步骤:

初始化result 用于存储最终的结果字符串。 - start 用于记录当前连续递增序列的起始索引。 遍历: 使用 for 循环遍历 plates 数组。 检查当前盘子是否可以加入当前序列,或者是否已经遍历到最后一个盘子。 处理序列: 如果当前序列长度大于等于3,将其格式化为 start-end 的形式并加入 result。 否则,将每个盘子单独加入 result返回结果: 使用 ",".join(result) 将 result 列表中的内容连接成一个字符串并返回。

代码如下:

def solution(plates: list[int], n: int) -> str: # 初始化结果列表 result = [] # 初始化当前序列的起始索引 start = 0

# 遍历盘子数组
for i in range(1, n):
    # 如果当前盘子不能加入当前序列,或者已经遍历到最后一个盘子
    if plates[i] != plates[i - 1] + 1:
        # 计算当前序列的长度
        length = i - start
        # 如果当前序列长度大于等于3,加入结果列表
        if length >= 3:
            result.append(f"{plates[start]}-{plates[i - 1]}")
        else:
            # 否则,将每个盘子单独加入结果列表
            for j in range(start, i):
                result.append(str(plates[j]))
        # 更新起始索引
        start = i

# 最后一个序列
length = n - start
if length >= 3:
    result.append(f"{plates[start]}-{plates[n - 1]}")
else:
    for j in range(start, n):
        result.append(str(plates[j]))

# 将结果列表转换为字符串并返回
return ",".join(result)

if __name__ == "__main__":
print(solution([-3, -2, -1, 2, 10, 15, 16, 18, 19, 20], 10) == "-3--1,2,10,15,16,18-20")
print(solution([-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20], 20) == "-6,-3-1,3-5,7-11,14,15,17-20")
print(solution([1, 2, 7, 8, 9, 10, 11, 19], 8) == "1,2,7-11,19")

感想:

通过解决这道经典题目,我锻炼了自己的逻辑思维能力,学会如何处理数组的边界条件,并提高代码的效率和可读性。