问题:
All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACGAATTCCG". When studying DNA, it is sometimes useful to identify repeated sequences within the DNA.
Write a function to find all the 10-letter-long sequences (substrings) that occur more than once in a DNA molecule.
For example,
Given s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT", Return: ["AAAAACCCCC", "CCCCCAAAAA"].
Given s = "ACGTACGTACGTACGT", Return: ["ACGTACGTAC","CGTACGTACG","GTACGTACGT"]
解决:
① 验证DNA中重复的子串,使用hash表,将出现过的结果保存在hashmap中,并记录其出现的次数,根据其出现次数进行相应的处理。时间 O(N) 空间 O(N)
class Solution {//49ms
public List<String> findRepeatedDnaSequences(String s) {
List<String> res = new ArrayList<>();
Map<String,Integer> map = new HashMap<>();
for (int i = 10;i <= s.length();i ++){//从第10位开始作为结尾,唯一一位,比较一次子串
String substr = s.substring(i - 10,i);
if (map.containsKey(substr)){
if (map.get(substr) == 1){//第一次遇到,加入结果集
res.add(substr);
}
map.put(substr,2);//标记为已经遇到过一次了
}else{//如果不存在,则加入表中
map.put(substr,1);
}
}
return res;
}
}
② 使用hash表和位操作。
构成输入字符串的字符只有四种,分别是A, C, G, T,首先考虑将其进行二进制编码:
A -> 00
C -> 01
G -> 10
T -> 11
在编码的情况下,每10位字符串的组合即为一个数字,且10位的字符串有20位;一般来说int有4个字节,32位,即可以用于对应一个10位的字符串。例如:
ACGTACGTAC -> 00011011000110110001
AAAAAAAAAA -> 00000000000000000000
20位的二进制数,至多有2^20种组合,因此hash table的大小为2^20,即1024 * 1024,将hash table设计为bool hashTable[1024 * 1024];
遍历字符串: 每次向右移动1位字符,相当于字符串对应的int值左移2位,再将其最低2位置为新的字符的编码值,最后将高2位置0。时间 O(N) 空间 O(N)。
class Solution { //76ms
public List<String> findRepeatedDnaSequences(String s) {
List<String> res = new ArrayList<>();
HashMap<Integer,Integer> map = new HashMap<>();
for (int i = 10;i <=s.length();i ++){
String sub = s.substring(i - 10,i);
int code = encode(sub);
if (map.containsKey(code)){
if (map.get(code) == 1){
res.add(sub);
}
map.put(code,2);
}else{
map.put(code,1);
}
}
return res;
}
public int encode(String str){
int code = 0;
for (int i = 0;i < str.length();i ++){
char c = str.charAt(i);
code <<= 2;//每两位表示一个字符
switch (c){
case 'A': code += 0; break;
case 'C': code += 1; break;
case 'T': code += 2; break;
case 'G': code += 3; break;
}
}
return code;
}
}
③ 使用位操作进行编码,使用hash table保存已经编码的结果。
class Solution{//48ms
public List<String> findRepeated(String s){
List<String> res = new ArrayList<>();
if (s == null || s.length() == 0) {
return res;
}
Set<Integer> ones = new HashSet<>();
Set<Integer> twos = new HashSet<>();
//对字母进行编码
int[] map = new int[256];
map['A'] = 0;
map['C'] = 1;
map['G'] = 2;
map['T'] = 3;
for (int i = 0;i < s.length() - 9 ;i ++ ) {
int val = 0;
//对子串进行编码
for (int j = i;j < i + 10 ;j ++ ) {
val <<= 2;
val = val | map[s.charAt(j)];
}
if (! ones.add(val) && twos.add(val)) {
res.add(s.substring(i,i + 10));
}
}
return res;
}
}
④ 在discuss中看到的。
class Solution{//9ms
public List<String> findRepeatedDnaSequences(String s){
List<String> res = new ArrayList<>();
if (s == null || s.length() < 10) {
return res;
}
//对字母进行编码
char[] map = new char[256];
map['A'] = 0;
map['C'] = 1;
map['G'] = 2;
map['T'] = 3;
int mask = 0xfffff;//20bit,10个字母,每个字母占2bit
int val = 0;
char[] schar = s.toCharArray();
for (int i = 0;i < 9 ;i ++ ) {//对前9位进行编码
val = (val << 2) | (map[schar[i]] & 3);
}
byte[] bytes = new byte[1 << 20];
for (int i = 9;i < schar.length ;i ++ ) {
val = ((val << 2) & mask) | ((map[schar[i]]) & 3);//编码
if (bytes[val] == 1) {
res.add(String.valueOf(schar,i - 9,10));
}
if (bytes[val] < 2) {
bytes[val] ++;
}
}
return res;
}
}
class Solution{//20ms ---> 可以看出,数据的类型对运行的效率也有影响
public List<String> findRepeatedDnaSequences(String s){
List<String> res = new ArrayList<>();
if (s == null || s.length() < 10) {
return res;
}
//对字母进行编码
int[] map = new int[256];
map['A'] = 0;
map['C'] = 1;
map['G'] = 2;
map['T'] = 3;
int mask = 0xfffff;//20bit,10个字母,每个字母占2bit
int val = 0;
char[] schar = s.toCharArray();
for (int i = 0;i < 9 ;i ++ ) {//对前9位进行编码
val = (val << 2) | (map[schar[i]] & 3);
}
int[] num = new int[1 << 20];//记录对应位置的编码出现的个数
for (int i = 9;i < schar.length ;i ++ ) {
val = ((val << 2) & mask) | ((map[schar[i]]) & 3);//编码
if (num[val] == 1) {
res.add(String.valueOf(schar,i - 9,10));
}
if (num[val] < 2) {
num[val] ++;
}
}
return res;
}
}