leetcode【87】【tag string】Repeated DNA Sequences【c++版本,時間92%,空間100%,位運算,hash表】

問題描述:

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());
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章