C++實現(xiàn)LeetCode(187.求重復(fù)的DNA序列)
[LeetCode] 187. Repeated DNA Sequences 求重復(fù)的DNA序列
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.
Example:
Input: s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT"
Output: ["AAAAACCCCC", "CCCCCAAAAA"]
看到這道題想到這應(yīng)該屬于 CS 的一個重要分支生物信息 Bioinformatics 研究的內(nèi)容,研究 DNA 序列特征的重要意義自然不用多說,但是對于我們廣大碼農(nóng)來說,還是專注于算法吧,此題還是用位操作 Bit Manipulation 來求解,計算機由于其二進制存儲的特點可以很巧妙的解決一些問題,像之前的 Single Number 和 Single Number II 都是很巧妙利用位操作來求解。此題由于構(gòu)成輸入字符串的字符只有四種,分別是 A, C, G, T,下面來看下它們的 ASCII 碼用二進制來表示:
A: 0100 0001 C: 0100 0011 G: 0100 0111 T: 0101 0100
由于目的是利用位來區(qū)分字符,當然是越少位越好,通過觀察發(fā)現(xiàn),每個字符的后三位都不相同,故而可以用末尾三位來區(qū)分這四個字符。而題目要求是 10 個字符長度的串,每個字符用三位來區(qū)分,10 個字符需要30位,在 32 位機上也 OK。為了提取出后 30 位,還需要用個 mask,取值為 0x7ffffff,用此 mask 可取出后27位,再向左平移三位即可。算法的思想是,當取出第十個字符時,將其存在 HashMap 里,和該字符串出現(xiàn)頻率映射,之后每向左移三位替換一個字符,查找新字符串在 HashMap 里出現(xiàn)次數(shù),如果之前剛好出現(xiàn)過一次,則將當前字符串存入返回值的數(shù)組并將其出現(xiàn)次數(shù)加一,如果從未出現(xiàn)過,則將其映射到1。為了能更清楚的闡述整個過程,就用題目中給的例子來分析整個過程:
首先取出前九個字符 AAAAACCCC,根據(jù)上面的分析,用三位來表示一個字符,所以這九個字符可以用二進制表示為 001001001001001011011011011,然后繼續(xù)遍歷字符串,下一個進來的是C,則當前字符為 AAAAACCCCC,二進制表示為 001001001001001011011011011011,然后將其存入 HashMap 中,用二進制的好處是可以用一個 int 變量來表示任意十個字符序列,比起直接存入字符串大大的節(jié)省了內(nèi)存空間,然后再讀入下一個字符C,則此時字符串為 AAAACCCCCA,還是存入其二進制的表示形式,以此類推,當某個序列之前已經(jīng)出現(xiàn)過了,將其存入結(jié)果 res 中即可,參見代碼如下:
解法一:
class Solution { public: vector<string> findRepeatedDnaSequences(string s) { vector<string> res; if (s.size() <= 10) return res; int mask = 0x7ffffff, cur = 0; unordered_map<int, int> m; for (int i = 0; i < 9; ++i) { cur = (cur << 3) | (s[i] & 7); } for (int i = 9; i < s.size(); ++i) { cur = ((cur & mask) << 3) | (s[i] & 7); if (m.count(cur)) { if (m[cur] == 1) res.push_back(s.substr(i - 9, 10)); ++m[cur]; } else { m[cur] = 1; } } return res; } };
上面的方法可以寫的更簡潔一些,這里可以用 HashSet 來代替 HashMap,只要當前的數(shù)已經(jīng)在 HashSet 中存在了,就將其加入 res 中,這里 res 也定義成 HashSet,這樣就可以利用 HashSet 的不能有重復(fù)項的特點,從而得到正確的答案,最后將 HashSet 轉(zhuǎn)為 vector 即可,參見代碼如下
解法二:
class Solution { public: vector<string> findRepeatedDnaSequences(string s) { unordered_set<string> res; unordered_set<int> st; int cur = 0; for (int i = 0; i < 9; ++i) cur = cur << 3 | (s[i] & 7); for (int i = 9; i < s.size(); ++i) { cur = ((cur & 0x7ffffff) << 3) | (s[i] & 7); if (st.count(cur)) res.insert(s.substr(i - 9, 10)); else st.insert(cur); } return vector<string>(res.begin(), res.end()); } };
上面的方法都是用三位來表示一個字符,這里可以用兩位來表示一個字符,00 表示A,01 表示C,10 表示G,11 表示T,那么總共需要 20 位就可以表示十個字符流,其余的思路跟上面的方法完全相同,注意這里的 mask 只需要表示 18 位,所以變成了 0x3ffff,參見代碼如下:
解法三:
class Solution { public: vector<string> findRepeatedDnaSequences(string s) { unordered_set<string> res; unordered_set<int> st; unordered_map<int, int> m{{'A', 0}, {'C', 1}, {'G', 2}, {'T', 3}}; int cur = 0; for (int i = 0; i < 9; ++i) cur = cur << 2 | m[s[i]]; for (int i = 9; i < s.size(); ++i) { cur = ((cur & 0x3ffff) << 2) | (m[s[i]]); if (st.count(cur)) res.insert(s.substr(i - 9, 10)); else st.insert(cur); } return vector<string>(res.begin(), res.end()); } };
如果不需要考慮節(jié)省內(nèi)存空間,那可以直接將 10個 字符組成字符串存入 HashSet 中,那么也就不需要 mask 啥的了,但是思路還是跟上面的方法相同:
解法四:
class Solution { public: vector<string> findRepeatedDnaSequences(string s) { unordered_set<string> res, st; for (int i = 0; i + 9 < s.size(); ++i) { string t = s.substr(i, 10); if (st.count(t)) res.insert(t); else st.insert(t); } return vector<string>{res.begin(), res.end()}; } };
到此這篇關(guān)于C++實現(xiàn)LeetCode(187.求重復(fù)的DNA序列)的文章就介紹到這了,更多相關(guān)C++實現(xiàn)求重復(fù)的DNA序列內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Qt QChart 創(chuàng)建圖表的實現(xiàn)方法
這篇文章主要介紹了Qt QChart 創(chuàng)建圖表的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12C++最短路徑Dijkstra算法的分析與具體實現(xiàn)詳解
經(jīng)典的求解最短路徑算法有這么幾種:廣度優(yōu)先算法、Dijkstra算法、Floyd算法。本文是對?Dijkstra算法的總結(jié),該算法適用于帶權(quán)有向圖,可求出起始頂點到其他任意頂點的最小代價以及對應(yīng)路徑,希望對大家有所幫助2023-03-03C++ Qt開發(fā)之使用QProcess實現(xiàn)進程管理
Qt 是一個跨平臺C++圖形界面開發(fā)庫,利用Qt可以快速開發(fā)跨平臺窗體應(yīng)用程序,本文將重點介紹如何運用QProcess組件實現(xiàn)針對進程的控制管理等,感興趣的可以了解下2024-03-03C++中STL的優(yōu)先隊列priority_queue詳解
這篇文章主要介紹了C++中STL的優(yōu)先隊列priority_queue詳解,今天講一講優(yōu)先隊列(priority_queue),實際上,它的本質(zhì)就是一個heap,我從STL中扒出了它的實現(xiàn)代碼,需要的朋友可以參考下2023-08-08