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"].
全部的DNA都是有A,C,G,T這四個字母組成的,好比「ACGAATTCCG」。在研究DNA的時候,從DNA序列中找到重複出現的模式是頗有用的。這個問題要求咱們在一個DNA序列中找到出現超過兩次的長度爲10的子序列。面試
好比,假設DNA序列爲AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT,那麼找到的知足條件的子序列爲["AAAAACCCCC", "CCCCCAAAAA"]算法
其實,咱們只須要找到全部的長度爲10的子序列而且判斷這些子序列是否存在重複就能夠了。若是不考慮生成子字符串的過程,那麼這個算法只須要遍歷一次字符串就能夠完成了。
代碼以下:數組
public List<String> findRepeatedDnaSequences(String s) { //記錄不是第一次遍歷到的結果 Set<String> result = new HashSet<String>(); //記錄第一次遍歷到的結果 Set<String> visited = new HashSet<String>(); for(int i = 0 ; i<s.length()-9 ; i++){ //得到以i做爲起點的長度爲10的字符串 String cur = s.substring(i, i+10); if(!visited.add(cur)){ result.add(cur); } } return new ArrayList<String>(result); }
上一題的思路其實基本沒有問題,惟一的缺點是,一個長度爲n的字符串可以產生n-9個長度爲10的子字符串。這n-9個子字符串所佔用的空間將會遠遠超過n-9個整數所佔用的空間。若是之間存儲字符串,那麼極可能會形成內存溢出。所以咱們須要考慮將字符串轉化爲整數並存儲。微信
其實若是是將26個字母所有轉化爲整數,並用整數表示任意10個字母所組成的字符串是不可能的。由於26個字母意味着每一個字母至少須要5位才能表示出來。好比00000
表明A, 00001
表明B。而10個字母意味着須要5*10=50位,而一個整形是32位。ide
而本題中,只有四個字母A,C,G和T,只須要兩位就能夠表示這四個字母,分別是:
A----00----0
C----01----1
G----10----2
T----11----3性能
那麼ACGAATTCCG對應的二進制碼就是00 01 10 00 00 11 11 01 01 10, 再將這個二進制數轉換成對應的十進制數。由於每一個字符串對應的二進制長度爲20,小於整數的32,所以是可行的。spa
代碼以下:code
public List<String> findRepeatedDnaSequences2(String s){ List<String> result = new ArrayList<String>(); Set<Integer> firstTime = new HashSet<Integer>(); Set<Integer> secondTime = new HashSet<Integer>(); //也可使用hashmap,可是用數組的話能夠很好的提高性能 char[] map = new char[26]; map['A'-'A'] = 0;//00 map['C'-'A'] = 1;//01 map['G'-'A'] = 2;//10 map['T'-'A'] = 3;//11 char[] sArray = s.toCharArray(); for(int i = 0 ; i<sArray.length-9 ; i++){ int v = 0; for(int j = i ; j<i+10 ; j++){ v <<= 2; v |= map[sArray[j] - 'A']; } //這裏使用了短路原理。也就是在字符串被第一次添加到firstTime的時候,將不會觸發secondTime的添加 //而第二次遍歷時,會觸發secondTime的添加行爲並將子字符串添加到結果集中 //超過第二次遍歷覺得這secondTime的添加會返回false,說明已經添加過,從而避免了重複添加至結果集 if(!firstTime.add(v) && secondTime.add(v)){ result.add(s.substring(i, i+10)); } } return result; }
想要了解更多開發技術,面試教程以及互聯網公司內推,歡迎關注個人微信公衆號!將會不按期的發放福利哦~教程