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"]

看到这道题想到这应该属于 CS 的一个重要分支生物信息 Bioinformatics 研究的内容,研究 DNA 序列特征的重要意义自然不用多说,但是对于我们广大码农来说,还是专注于算法吧,此题还是用位操作 Bit Manipulation 来求解,计算机由于其二进制存储的特点可以很巧妙的解决一些问题,像之前的 Single Number 和 Single Number II 都是很巧妙利用位操作来求解。此题由于构成输入字符串的字符只有四种,分别是 A, C, G, T,下面来看下它们的 ASCII 码用二进制来表示:

A: 0100 0001  C: 0100 0011  G: 0100 0111  T: 0101 0100

由于目的是利用位来区分字符,当然是越少位越好,通过观察发现,每个字符的后三位都不相同,故而可以用末尾三位来区分这四个字符。而题目要求是 10 个字符长度的串,每个字符用三位来区分,10 个字符需要30位,在 32 位机上也 OK。为了提取出后 30 位,还需要用个 mask,取值为 0x7ffffff,用此 mask 可取出后27位,再向左平移三位即可。算法的思想是,当取出第十个字符时,将其存在 HashMap 里,和该字符串出现频率映射,之后每向左移三位替换一个字符,查找新字符串在 HashMap 里出现次数,如果之前刚好出现过一次,则将当前字符串存入返回值的数组并将其出现次数加一,如果从未出现过,则将其映射到1。为了能更清楚的阐述整个过程,就用题目中给的例子来分析整个过程:

首先取出前九个字符 AAAAACCCC,根据上面的分析,用三位来表示一个字符,所以这九个字符可以用二进制表示为 001001001001001011011011011,然后继续遍历字符串,下一个进来的是C,则当前字符为 AAAAACCCCC,二进制表示为 001001001001001011011011011011,然后将其存入 HashMap 中,用二进制的好处是可以用一个 int 变量来表示任意十个字符序列,比起直接存入字符串大大的节省了内存空间,然后再读入下一个字符C,则此时字符串为 AAAACCCCCA,还是存入其二进制的表示形式,以此类推,当某个序列之前已经出现过了,将其存入结果 res 中即可,参见代码如下:

解法一:

class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
vector<string> res;
if (s.size() <= ) return res;
int mask = 0x7ffffff, cur = ;
unordered_map<int, int> m;
for (int i = ; i < ; ++i) {
cur = (cur << ) | (s[i] & );
}
for (int i = ; i < s.size(); ++i) {
cur = ((cur & mask) << ) | (s[i] & );
if (m.count(cur)) {
if (m[cur] == ) res.push_back(s.substr(i - , ));
++m[cur];
} else {
m[cur] = ;
}
}
return res;
}
};

上面的方法可以写的更简洁一些,这里可以用 HashSet 来代替 HashMap,只要当前的数已经在 HashSet 中存在了,就将其加入 res 中,这里 res 也定义成 HashSet,这样就可以利用 HashSet 的不能有重复项的特点,从而得到正确的答案,最后将 HashSet 转为 vector 即可,参见代码如下:

解法二:

class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
unordered_set<string> res;
unordered_set<int> st;
int cur = ;
for (int i = ; i < ; ++i) cur = cur << | (s[i] & );
for (int i = ; i < s.size(); ++i) {
cur = ((cur & 0x7ffffff) << ) | (s[i] & );
if (st.count(cur)) res.insert(s.substr(i - , ));
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', }, {'C', }, {'G', }, {'T', }};
int cur = ;
for (int i = ; i < ; ++i) cur = cur << | m[s[i]];
for (int i = ; i < s.size(); ++i) {
cur = ((cur & 0x3ffff) << ) | (m[s[i]]);
if (st.count(cur)) res.insert(s.substr(i - , ));
else st.insert(cur);
}
return vector<string>(res.begin(), res.end());
}
};

如果不需要考虑节省内存空间,那可以直接将 10个 字符组成字符串存入 HashSet 中,那么也就不需要 mask 啥的了,但是思路还是跟上面的方法相同:

解法四:

class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
unordered_set<string> res, st;
for (int i = ; i + < s.size(); ++i) {
string t = s.substr(i, );
if (st.count(t)) res.insert(t);
else st.insert(t);
}
return vector<string>{res.begin(), res.end()};
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/187

参考资料:

https://leetcode.com/problems/repeated-dna-sequences/

https://leetcode.com/problems/repeated-dna-sequences/discuss/53855/7-lines-simple-java-on

https://leetcode.com/problems/repeated-dna-sequences/discuss/53877/i-did-it-in-10-lines-of-c

https://leetcode.com/problems/repeated-dna-sequences/discuss/53867/clean-java-solution-hashmap-bits-manipulation

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] 187. Repeated DNA Sequences 求重复的DNA序列的更多相关文章

  1. leetcode 187. Repeated DNA Sequences 求重复的DNA串 ---------- java

    All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACG ...

  2. [LeetCode] Repeated DNA Sequences 求重复的DNA序列

    All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACG ...

  3. [LeetCode] 128. Longest Consecutive Sequence 求最长连续序列

    Given an unsorted array of integers, find the length of the longest consecutive elements sequence. F ...

  4. [leetcode]187. Repeated DNA Sequences寻找DNA中重复出现的子串

    很重要的一道题 题型适合在面试的时候考 位操作和哈希表结合 public List<String> findRepeatedDnaSequences(String s) { /* 寻找出现 ...

  5. [LeetCode] 187. Repeated DNA Sequences 解题思路

    All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACG ...

  6. Java for LeetCode 187 Repeated DNA Sequences

    All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACG ...

  7. [LeetCode#187]Repeated DNA Sequences

    Problem: All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: ...

  8. LeetCode 187. 重复的DNA序列(Repeated DNA Sequences)

    187. 重复的DNA序列 187. Repeated DNA Sequences 题目描述 All DNA is composed of a series of nucleotides abbrev ...

  9. 【Leetcode】【Medium】Repeated DNA Sequences

    All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACG ...

随机推荐

  1. SpringDataRedis简单入门介绍

    1:问题引入 在实际开发中,开发的每一个项目,每天都有大量的人访问,对数据库造成很大的访问压力,甚至是瘫痪.那如何解决呢?我们通常的做法有两种:一种是数据缓存.一种是网页静态化.我们今天讨论第一种解决 ...

  2. Window权限维持(六):BITS Jobs

    Windows操作系统包含各种实用程序,系统管理员可以使用它们来执行各种任务.这些实用程序之一是后台智能传输服务(BITS),它可以促进文件到Web服务器(HTTP)和共享文件夹(SMB)的传输能力. ...

  3. 微信小程序小Demo

    微信小程序小Demo 调用API,轮播图,排行榜,底部BabTar的使用... board // board/board.js Page({ /** * 页面的初始数据 */ // 可以是网络路径图片 ...

  4. C# Task,new Task().Start(),Task.Run();TTask.Factory.StartNew

    1. Task task = new Task(() => { MultiplyMethod(a, b); }); task.Start(); 2. Task task = Task.Run(( ...

  5. 使用 Floccus 插件和坚果云同步 Chrome 类浏览器书签

    使用 Floccus 插件和坚果云同步 Chrome 类浏览器书签 魏刘宏  2019 年 11 月 22 日 如题,本文讨论在使用 Chromium 内核的浏览器上,使用 Floccus 插件,配合 ...

  6. Jsp和Servlet有什么区别?

    Servlet接口中有哪些方法? Servlet接口定义了5个方法,其中前三个方法与Servlet生命周期相关: void init(ServletConfig config) throws Serv ...

  7. python3之int类的常用方法练习

    int类的常用方法练习 #coding:utf-8 #int类的常用方法 num1 = 18 num2 = -15 #查询创建num1所用的类 print(type(num1)) #num1占用的最小 ...

  8. jmeter对tomcat性能测试

    主要对tomcat的参数做一些记录(jmeter和tomcat在同一个计算机,可能引起测试误差) 我的计算机配置  4核8线程  8G内存 案例一 tomcat  JVM  1.8G堆内存,无数据库操 ...

  9. 【转载】Visual Studio2017如何设置打包发布的WinForm应用程序的版本号

    在Visual Studio 2017集成开发工具中,打包发布Winform窗体应用程序的时候,支持设置此次打包发布的Winform窗体应用程序对应的版本号信息,并且支持一次设置后,后续的所有发布版本 ...

  10. laravel报错 No query results for model . 的解决方法

    这个通常由路由绑定出的问题,注意有绑定模型的路由,同路径的路由需要放在没绑定路由的后面 例如:/product/comments和/product的是同路径,/product必须放在/product/ ...