4.环状 DNA 序列整理
环状 DNA 又称超螺旋,即一段碱基序列呈现环状,在分析时,需要将相同序列的环状 DNA 分到相同组内,现需将环状碱基序列按照最小表示法进行排序。
一段长度为 n 的碱基序列,按照顺时针方向,碱基序列可以从任意位置起开始该序列顺序,因此长度为 n 的碱基序列有 n 种表示法。例如:长度为 6 的碱基序列 CGAGTC,有 CGAGTC、GAGTCC、AGTCCG 等表示法。在这些表示法中,字典序最小的称为“最小表示”。
输入一个长度为 n(n <= 100)的环状碱基序列(只包含 A、C、G、T 这 4 种碱基)的一种表示法,输出该环状碱基序列的最小表示。
样例说明
ATCA 的表示有 ATCA、TCAA、CAAT、AATC,最小表示(根据字典序)是AATC。
CGAGTC 的最小表示是 AGTCCG。
输入格式
一段 DNA 碱基序列
输出格式
DNA 碱基序列的最小表示
输入样例
ATCA
输出样例
AATC
解题思路
-
构造倍长字符串:将输入字符串
dna_sequence与自身拼接形成doubled字符串,这样可以模拟环状结构。因为我们可以从任意位置开始,因此拼接后的倍长字符串doubled包含了所有可能的旋转子串。 -
双指针法:设定两个指针
left和right。left指向当前字典序最小子串的起始位置,而right从left + 1开始,逐一比较其他可能的旋转起始位置。 -
比较子串的字典序:使用
compareSubstrings方法,对left和right开始的长度为n的子串逐字符进行比较。如果right指向的子串字典序更小,则更新left为right。 -
截取结果:遍历完所有可能的起始位置后,
left会指向字典序最小的起始位置,截取并返回该位置开始的长度为n的子串。
算法实现
public class Main {
public static String solution(String dna_sequence) {
// Step 1: 获取输入序列的长度
int n = dna_sequence.length();
// Step 2: 构造倍长字符串
String doubled = dna_sequence + dna_sequence;
// Step 3: 初始化左右指针
int left = 0; // 当前字典序最小子串的起始位置
int right = 1; // 下一个起始位置,用于比较
// Step 4: 使用双指针找到字典序最小的起始位置
while (right < n) {
if (compareSubstrings(doubled, n, left, right) == 1) {
left = right;
}
right++;
}
// Step 5: 从最小字典序位置开始截取长度为 n 的子串
return doubled.substring(left, left + n);
}
// 辅助方法,用于比较两个起点的字典序子串
public static int compareSubstrings(String doubled, int n, int left, int right) {
// 按照字典序逐个字符比较长度为 n 的子串
for (int i = 0; i < n; i++) {
char leftChar = doubled.charAt(left + i);
char rightChar = doubled.charAt(right + i);
if (leftChar < rightChar) {
return -1; // left子串更小
} else if (leftChar > rightChar) {
return 1; // right子串更小
}
}
return 0; // 如果所有字符相等,则子串相等
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution("ATCA").equals("AATC")); // 输出: AATC
System.out.println(solution("CGAGTC").equals("AGTCCG")); // 输出: AGTCCG
System.out.println(solution("TCATGGAGTGCTCCTGGAGGCTGAGTCCATCTCCAGTAG").equals("AGGCTGAGTCCATCTCCAGTAGTCATGGAGTGCTCCTGG"));
}
}