問題描述:
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"]
源碼:
先看一下我自己的方法把,比較cuo,就是用一個hash存着之前出現過一次的字符串,如果出現第二次就存到result中,出現第三次就不管他了。
class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
int n = s.length();
vector<string> result;
if(n<=10) return result;
unordered_map<string, bool> st;
for(int i=0; i<=n-10; i++){
string sub = s.substr(i, 10);
// cout<<sub<<st[sub]<<endl;
if (!st.count(sub)) st[sub] = false;
else if (!st[sub]){
result.push_back(sub);
st[sub] = true;
}
else continue;
}
return result;
}
};
我得方法缺陷太過明顯,本來hash就佔空間,還要存字符串,而且substr不見得是個好方法。我翻了一下網站上別人的做法,有幾個比較好的拿出來說道說道:
由於目的是利用位來區分字符,當然是越少位越好,通過觀察發現,每個字符的後三位都不相同,故而可以用末尾三位來區分這四個字符。而題目要求是 10 個字符長度的串,每個字符用三位來區分,10 個字符需要30位,在 32 位機上也 OK。爲了提取出後 30 位,還需要用個 mask,取值爲 0x7ffffff,用此 mask 可取出後27位,再向左平移三位即可。算法的思想是,當取出第十個字符時,將其存在 HashMap 裏,和該字符串出現頻率映射,之後每向左移三位替換一個字符,查找新字符串在 HashMap 裏出現次數,如果之前剛好出現過一次,則將當前字符串存入返回值的數組並將其出現次數加一,如果從未出現過,則將其映射到1。爲了能更清楚的闡述整個過程,就用題目中給的例子來分析整個過程:
首先取出前九個字符 AAAAACCCC,根據上面的分析,用三位來表示一個字符,所以這九個字符可以用二進制表示爲 001001001001001011011011011,然後繼續遍歷字符串,下一個進來的是C,則當前字符爲 AAAAACCCCC,二進制表示爲 001001001001001011011011011011,然後將其存入 HashMap 中,用二進制的好處是可以用一個 int 變量來表示任意十個字符序列,比起直接存入字符串大大的節省了內存空間,然後再讀入下一個字符C,則此時字符串爲 AAAACCCCCA,還是存入其二進制的表示形式,以此類推,當某個序列之前已經出現過了,將其存入結果 res 中即可
這樣空間瞬間就到了100%,時間82%
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;
}
};
也可以考慮用set。時間92%,空間100%,可能是少了判斷的一步。
class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
int n = s.length();
unordered_set<string> result;
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<n; i++){
cur = ((cur & 0x7ffffff) << 3) | (s[i] & 7);
if(st.count(cur)) result.insert(s.substr(i-9, 10));
else st.insert(cur);
}
return vector<string>(result.begin(), result.end());
}
};
可以考慮用兩位表示字符。00 表示A,01 表示C,10 表示G,11 表示T
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());
}
};