当前位置: 首页 > news >正文

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

环状 DNA 序列的最小表示法解析

问题概述

在这个问题中,我们面对的是一个关于环状 DNA 序列的问题。由于 DNA 序列是环状的,所以可以从任何位置开始读取序列,这导致了同一个序列有多种不同的表示方式。我们的任务是找出这些表示方式中字典序最小的一个,即所谓的“最小表示”。

思路分析
  1. 生成所有可能的表示:最直接的方法是生成所有可能的序列表示,并比较它们的字典序。然而,对于较长的序列,这种方法的时间复杂度会非常高,因为它需要生成和比较大量的字符串。

  2. 优化策略:考虑到 DNA 序列的循环特性,我们可以利用字符串的旋转和比较操作来优化这个问题。具体来说,我们可以固定一个起始位置,然后通过比较不同起始位置得到的旋转字符串的字典序,来找到最小的表示。

  3. 字典序比较:在比较两个字符串时,我们只需要比较它们的前缀,直到找到第一个不同的字符。这个字符的 ASCII 值较小的字符串,其字典序也更小。

  4. 避免重复计算:由于 DNA 序列是环状的,所以某些旋转字符串的后缀和前缀是重叠的。我们可以利用这个特性来减少不必要的比较。

代码详解
public class MinimumRepresentationOfCircularDNA {// 方法:找到环状 DNA 序列的最小表示法public static String findMinimumRepresentation(String dnaSequence) {int n = dnaSequence.length();int i = 0, j = 1, k = 0;// 使用 Floyd 的循环字符串匹配算法(也称为“乌龟与兔子”算法)来找到最小表示while (i < n && j < n) {// 比较从 i 和 j 开始的两个子串while (k < n && dnaSequence.charAt((i + k) % n) == dnaSequence.charAt((j + k) % n)) {k++;}// 如果 k 已经遍历完整个字符串,说明两个子串完全相同,此时可以随意选择一个作为起点(但实际上不会进入这个分支,因为后续会有 i 或 j 的移动)if (k == n) {break;}// 根据字典序大小移动指针if (dnaSequence.charAt((i + k) % n) > dnaSequence.charAt((j + k) % n)) {i = (i + k + 1) % n; // i 移动到下一个不同字符的下一位(考虑循环)} else {j = (j + k + 1) % n; // j 移动到下一个不同字符的下一位(考虑循环)// 如果 i 和 j 相遇,说明找到了一个最小表示法的起点(但此时 i 和 j 相等,所以选择 i(或 j)并加上 k+1(即最小循环的起始位置到当前位置的偏移量,但这里不需要真的加,因为后续直接构建字符串)来构建最小表示)// 注意:由于 i 和 j 相遇时,我们已经知道从 i(或 j)开始的后缀是字典序最小的,所以我们只需要从 i(或 j)开始构建字符串即可// 同时,由于字符串是循环的,我们可以将 i(或 j)之后的部分与前面的部分拼接起来形成最小表示if (i == j) {j = (j + 1) % n; // 为了避免死循环(虽然在这个特定问题中不会发生,但加上这行代码可以使算法更加健壮)// 但实际上,在这个问题中,当 i == j 时,我们已经可以构建最小表示了,不需要再移动 j// 这行代码在这里是多余的,但为了保持与一些标准实现的一致性,我还是保留了它(并注释说明了其多余性)}// 注意:这里的 j 移动实际上是为了下一个循环做准备,而不是为了构建当前的最小表示// 构建最小表示是在 while 循环外部进行的}// 重置 k,为下一轮比较做准备k = 0;}// 由于 i 和 j 最终会指向同一个位置(或者相差 n 的整数倍,但在这个问题中,我们可以忽略这个细节),我们选择 i(或 j)作为起点来构建最小表示// 由于字符串是循环的,我们将从 i 开始的后缀与前面的部分拼接起来形成最小表示int startIndex = Math.min(i, j); // 实际上,i 和 j 最终会相等,所以这里取哪个都可以return dnaSequence.substring(startIndex) + dnaSequence.substring(0, startIndex);}// 主方法:测试 findMinimumRepresentation 方法public static void main(String[] args) {// 测试样例String dnaSequence1 = "ATCA";String dnaSequence2 = "CGAGTC";String dnaSequence3 = "TTGAC";String dnaSequence4 = "TCATGGAGTGCTCCTGGAGGCTGAGTCCATCTCCAGTAG";// 输出最小表示System.out.println("Minimum representation of \"" + dnaSequence1 + "\": " + findMinimumRepresentation(dnaSequence1)); // 应该输出 "AATC"System.out.println("Minimum representation of \"" + dnaSequence2 + "\": " + findMinimumRepresentation(dnaSequence2)); // 应该输出 "AGTCCG"System.out.println("Minimum representation of \"" + dnaSequence3 + "\": " + findMinimumRepresentation(dnaSequence3)); // 应该输出 "ACTTG"System.out.println("Minimum representation of \"" + dnaSequence4 + "\": " + findMinimumRepresentation(dnaSequence4)); // 应该输出 "AGGCTGAGTCCATCTCCAGTAGTCATGGAGTGCTCCTGG"(或循环等价的其他形式,但考虑到输出长度和可读性,这里给出的是完整形式)}
}

代码详解

  • 我们使用三个指针 i, j, k 来遍历和比较字符串。
  • ij 分别表示两个不同起始位置的索引。
  • k 用于比较从 ij 起始的字符串的前缀。
  • k 位置上的字符相同时,我们增加 k 的值,继续向后比较。
  • 当找到第一个不同的字符时,我们根据字典序大小移动 ij
  • 如果 ij 相遇,说明我们已经遍历了一圈,并且找到了一个可能的最小表示起点。但由于 ij 此时相等,我们只需要选择其中一个(这里选择 i)作为起点,并加上 k+1 个字符(即最小循环的起始位置到当前位置的字符数)来构成最小表示。
  • 最后,我们返回从最小表示起点开始的子串,这个子串就是我们要找的最小表示。
个人思考与分析

这个问题考察的是对字符串处理和比较算法的理解。通过生成所有可能的表示并比较它们的字典序,我们可以直观地解决这个问题,但这种方法的时间复杂度较高。通过优化策略,我们可以避免生成所有可能的表示,而是利用字符串的循环特性和字典序比较来找到最小表示。这种方法不仅降低了时间复杂度,还提高了代码的可读性和效率。

此外,这个问题还提醒我们,在处理具有循环特性的数据结构时,要善于利用这种特性来优化算法。通过固定一个起始位置,并比较不同起始位置得到的旋转字符串的字典序,我们可以找到满足条件的最小表示。这种方法在字符串处理、数组处理等领域都有广泛的应用。


http://www.mrgr.cn/news/65823.html

相关文章:

  • 大学适合学C语言还是Python?
  • 正则表达式(Regular Expressions)
  • Java学习Day57:碧水金睛兽!(Spring Cloud微服务1.0)
  • golang gin ShouldBind的介绍和使用
  • 克服奖励欺骗:Meta发布全新后训练方式CGPO,编程水平直升5%,打破RLHF瓶颈
  • 通过 codespaces + ipad 来进行算法训练
  • PGMP练-DAY16
  • Android沙箱
  • 不画饼——研究生学习和赚钱的平衡点
  • 【鸿蒙】开发者攻略:借力鸿蒙生态,打造全场景应用新体验
  • Javascipt基础__1
  • 嵌入式开发之文件I/O-函数
  • 1003-leetcode补打卡 最长公共前缀
  • 强网杯2024 Web WP
  • 制造业大模型应用案例赏析
  • [OS] sys_mmap() 函数+
  • FFMPEG录屏(21)--- Linux 下基于X11枚举所有可见窗口,并获取标题、图标、缩略图、进程路径等信息
  • 基于python flask的知乎问答文本分析与情感预测系统
  • Android使用scheme方式唤醒处于后台时的App场景
  • 【C++】继承的理解
  • 电脑虚拟机启动树莓派rviz
  • 【c++篇】:深入剖析vector--模拟实现属于自己的c++动态数组
  • SVD求解ICP旋转矩阵不正确处理
  • WorkFlow源码剖析——Communicator之TCPServer(中)
  • SpringBoot源码解析(一)
  • 响应式编程-reactor