Rosalind编程问题之排列组合字母的K-mers。
Enumerating k-mers Lexicographically
Problem:
Assume that an alphabet A has a predetermined order; that is, we write the alphabet as a permutation A=(a1,a2,…,ak), where a1<a2<⋯<ak. For instance, the English alphabet is organized as (A,B,…,Z).
Given two strings s and t having the same length n, we say that s precedes t in the lexicographic order (and write s<Lext) if the first symbol s[j] that doesn’t match t[j] satisfies sj<tj in A.
Given: A collection of at most 10 symbols defining an ordered alphabet, and a positive integer n (n≤10).
Sample input:
A C G T
2
Return: All strings of length n that can be formed from the alphabet, ordered lexicographically (use the standard order of symbols in the English alphabet).
Sample output:
AA
AC
AG
AT
CA
CC
CG
CT
GA
GC
GG
GT
TA
TC
TG
TT
简单概述下题目大意:给出一系列英文字母和数字n,需要输出长度为n的字母的所有组合。
看似非常简单的题目,但是背后需要深入学习的java语法非常细节。
先解析下解题思路:
1.读取所有英文字母(手动去掉间隔的空格)和k-mers长度n。
2.第一个循环读取全部英文字母到集合。
3.之后的循环逐一添加全部英文字母到原有的元素尾缀。
4.判断长度不为n的元素,并且将其删除。
5.增强for循环输出结果到屏幕。
下面是实现代码:
public class Enumerating_kmers_Lexicographically {
public static void main(String[] args) {
//1.输入组合长度n及待组合的字母
Scanner sc = new Scanner(System.in);
System.out.println("请输入组合长度n:");
int n = sc.nextInt();
Scanner sr = new Scanner(System.in);
System.out.println("请输入待组合的字母:");//网站给的文件有空格间隔,此处输入需要去掉空格。
String alphabet = sr.nextLine();
//2.添加元素到集合并进行最终输出
List<String> finalalphabet = new ArrayList<>();
kmers(alphabet, finalalphabet, n, 0);
}
public static void kmers(String alphabet, List<String> finalalphabet, int n, int i) {//初始默认i为0,意为从第一个字母开始遍历产生kmers
while (i < n) {
i += 1;
//从第二轮循环开始向原有元素累加新字符
if (i != 1) {
int size = finalalphabet.size();
for (int k = 0; k < size; k++) {
for (int j = 0; j < alphabet.length(); j++) {
finalalphabet.add(finalalphabet.get(k) + alphabet.charAt(j));
}
}
//使用列表迭代器循环删除元素
ListIterator<String> lit = finalalphabet.listIterator();
while (lit.hasNext()) {
String s = lit.next();
if (s.length() < i) {
lit.remove();//迭代器删除,可以防止删除改变ArrayList大小改变导致的并发修改异常。
}
}
} else {//第一轮循环直接添加元素进数组
for (int j = 0; j < alphabet.length(); j++) {
finalalphabet.add(String.valueOf(alphabet.charAt(j)));
}
}
}
for (String s : finalalphabet) {
System.out.println(s);
}
}
}
迭代器并发修改错误
撰写上述代码时,“使用列表迭代器循环删除元素”一步至关重要,可以方便后继遍历添加元素,同时及时清理内存。但是小编在写这一部分的时候会提示报错,原有的代码为:
for (String s : finalalphabet) {
if (s.length()<i){
finalalphabet.remove(s) ;
}
}
原因是当finalalphabet删除元素s之后,导致finalalphabet自身大小发生改变,for循环遍历就会产生ConcurrentModificationException并发修改异常。 不过Java非常贴心地准备了迭代器工具,通过迭代器遍历即可实现在不报错的情况下删除目标元素。具体方法参上。