Repeated DNA Sequences solution and explanation
9 more videos on this problem
Repeated DNA Sequences - Leetcode 187 - PythonNeetCode · 7:31 · 45K views
Repeated DNA Sequences | Leet code 187 | Theory explained + Python codeSai Anish Malla · 5:40 · 2K views
Solving LeetCode 187 in JavaScript (Repeated DNA Sequences)KA Education · 6:32 · 2K views
Leetcode - Repeated DNA Sequences (Python)Timothy H Chang · 4:46 · 1K views
Repeated DNA Sequences | LeetCode 187 | Two Pointers | Sliding Window | HashMapscodeTips · 8:37 · 1K views
Repeated DNA Sequences | Set | Hashing | LeetCode 187 | C++ | PythonClashing Coder · 13:48 · 626 views
Leetcode#187: Repeated DNA SequencesPatel_Anjali (learn with SWAG) · 11:49 · 625 views
Repeated DNA Sequences Explained | HashMap and Set Approaches | LeetCode 187 | GeekificGeekific · 3:04 · 533 views
Repeated DNA Sequences Leetcode 187 videoEasyLeetcode · 7:18 · 486 views
Repeated DNA Sequences | LeetCode 187 | October LeetCode Challenge | Day 17Learn Code Repeat · 4:42 · 474 views
The problem
The DNA sequence is composed of a series of nucleotides abbreviated as 'A', 'C', 'G', and 'T'.
- For example,
"ACGAATTCCG"is a DNA sequence.
When studying DNA, it is useful to identify repeated sequences within the DNA.
Given a string s that represents a DNA sequence, return all the 10-letter-long sequences (substrings) that occur more than once in a DNA molecule. You may return the answer in any order.
Example 1:
Input: s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT" Output: ["AAAAACCCCC","CCCCCAAAAA"]
Example 2:
Input: s = "AAAAAAAAAAAAA" Output: ["AAAAAAAAAA"]
Constraints:
1 <= s.length <= 105s[i]is either'A','C','G', or'T'.
Approach overview
Problem Overview: Given a DNA string containing characters A, C, G, and T, find every 10-letter-long sequence that appears more than once. The challenge is efficiently detecting repeated substrings of fixed length without comparing every possible pair.
Approach 1: Sliding Window with Hash Map (O(n) time, O(n) space)
Use a sliding window of length 10 and scan the string from left to right. For each index, extract the substring s[i:i+10] and store it in a hash table. The hash map tracks how many times each sequence appears. When a sequence’s count becomes 2, add it to the result list. Each substring lookup and update is an average O(1) hash operation, so the overall scan stays linear.
This approach is straightforward and easy to implement in most languages. The key insight is that the window size is fixed, so each position generates exactly one candidate substring. Hashing avoids expensive pairwise comparisons between sequences. The downside is memory usage: storing many 10-character strings increases space consumption, especially when the DNA string is large.
Approach 2: Bit Manipulation with Rolling Encoding (O(n) time, O(n) space)
DNA characters come from a set of four symbols, which means each can be encoded using 2 bits. Map A=00, C=01, G=10, and T=11. A 10-character sequence therefore fits into a 20-bit integer. Instead of storing substrings, maintain a rolling integer hash. Shift the current value left by 2 bits, add the new character’s encoding, and mask the result to keep only the latest 20 bits.
This technique acts like a compact bit manipulation rolling hash. Store each encoded sequence in a hash set to track whether it has appeared before. When the same 20-bit pattern appears again, record the corresponding substring as a repeated sequence. The rolling update makes each step O(1) while reducing memory compared to storing full strings.
Recommended for interviews: Start by explaining the sliding window with a hash map. It clearly demonstrates understanding of substring scanning and hash-based duplicate detection. After that, propose the bit manipulation optimization. Interviewers often look for this encoding trick because it reduces memory and demonstrates knowledge of rolling hashes and compact representations.
Approach 1: Sliding Window with Hash Map
This approach utilizes a sliding window technique combined with a hash map (or dictionary) to identify 10-letter-long sequences that appear more than once. You slide the window across the string and keep track of all sequences in a hash map. If a sequence is found more than once, it's added to the result list.
def findRepeatedDnaSequences(s):
seen, repeated = set(), set()
for i in range(len(s) - 9):
seq = s[i:i + 10]
if seq in seen:
repeated.add(seq)
else:
seen.add(seq)
return list(repeated)function findRepeatedDnaSequences(s) {
const seen = new Set();
const repeated = new Set();
for (let i = 0; i <= s.length - 10; i++) {
const seq = s.substring(i, i + 10);
if (seen.has(seq)) {
repeated.add(seq);
} else {
seen.add(seq);
}
}
return Array.from(repeated);
}This Python solution uses a set to track seen sequences and another set to track repeated sequences. It iterates over the string and extracts each 10-letter sequence using slicing. If a sequence has been seen before, it's added to the 'repeated' set.
Time Complexity: O(n), where n is the length of the string, due to the single pass over the string.
Space Complexity: O(n), as we store sequences in sets.
Approach 2: Bit Manipulation
This approach utilizes bit manipulation to represent sequences as numbers, which can help optimize space usage when compared to storing strings directly. Each nucleotide is encoded as 2 bits ('A' = 00, 'C' = 01, 'G' = 10, 'T' = 11), allowing the entire 10-letter sequence to fit within 20 bits.
#include <vector>
#include <string>
#include <unordered_set>
std::vector<std::string> findRepeatedDnaSequences(std::string s) {
std::unordered_set<int> seen, repeated;
std::vector<std::string> result;
int map[128] = {0};
map['A'] = 0;
map['C'] = 1;
map['G'] = 2;
map['T'] = 3;
int num = 0;
for (int i = 0; i < s.size(); ++i) {
num = ((num << 2) & 0xFFFFF) | map[s[i]];
if (i < 9) continue;
if (seen.count(num) && !repeated.count(num)) {
result.push_back(s.substr(i - 9, 10));
repeated.insert(num);
}
seen.insert(num);
}
return result;
}#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char** findRepeatedDnaSequences(char* s, int* returnSize) {
int seen[1 << 20] = {0};
int repeated[1 << 20] = {0};
char** result = malloc(100 * sizeof(char*));
*returnSize = 0;
int num = 0;
for (int i = 0; i < strlen(s); i++) {
num = ((num << 2) & ((1 << 20) - 1)) | (s[i] & 3);
if (i < 9) continue;
if (seen[num] == 1 && !repeated[num]) {
result[*returnSize] = strndup(s + i - 9, 10);
repeated[num] = 1;
(*returnSize)++;
}
seen[num] = 1;
}
return result;
}This C++ solution uses unordered sets to track sequences encoded as numbers. Each 10-letter sequence is converted into an integer using bit shifting and masking to ensure only 20 bits are used, allowing for efficient comparison and storage.
Time Complexity: O(n), because each character is processed once.
Space Complexity: O(1), due to using a fixed size for storing sequence encodings.
Approach 3: Hash Table
We define a hash table cnt to store the occurrence count of all substrings of length 10.
We iterate through all substrings of length 10 in the string s. For the current substring t, we update its count in the hash table. If the count of t is 2, we add it to the answer.
After the iteration, we return the answer array.
The time complexity is O(n times 10), and the space complexity is O(n times 10). Here, n is the length of the string s.
class Solution {
public List<String> findRepeatedDnaSequences(String s) {
Map<String, Integer> cnt = new HashMap<>();
List<String> ans = new ArrayList<>();
for (int i = 0; i < s.length() - 10 + 1; ++i) {
String t = s.substring(i, i + 10);
if (cnt.merge(t, 1, Integer::sum) == 2) {
ans.add(t);
}
}
return ans;
}
}function findRepeatedDnaSequences(s: string): string[] {
const n = s.length;
const cnt: Map<string, number> = new Map();
const ans: string[] = [];
for (let i = 0; i <= n - 10; ++i) {
const t = s.slice(i, i + 10);
cnt.set(t, (cnt.get(t) ?? 0) + 1);
if (cnt.get(t) === 2) {
ans.push(t);
}
}
return ans;
}public class Solution {
public IList<string> FindRepeatedDnaSequences(string s) {
var cnt = new Dictionary<string, int>();
var ans = new List<string>();
for (int i = 0; i < s.Length - 10 + 1; ++i) {
var t = s.Substring(i, 10);
if (!cnt.ContainsKey(t)) {
cnt[t] = 0;
}
if (++cnt[t] == 2) {
ans.Add(t);
}
}
return ans;
}
}func findRepeatedDnaSequences(s string) (ans []string) {
cnt := map[string]int{}
for i := 0; i < len(s)-10+1; i++ {
t := s[i : i+10]
cnt[t]++
if cnt[t] == 2 {
ans = append(ans, t)
}
}
return
}use std::collections::HashMap;
impl Solution {
pub fn find_repeated_dna_sequences(s: String) -> Vec<String> {
if s.len() < 10 {
return vec![];
}
let mut cnt = HashMap::new();
let mut ans = Vec::new();
for i in 0..s.len() - 9 {
let t = &s[i..i + 10];
let count = cnt.entry(t).or_insert(0);
*count += 1;
if *count == 2 {
ans.push(t.to_string());
}
}
ans
}
}Approach 4: Rabin-Karp String Matching Algorithm
This method essentially combines sliding window and hash. Similar to 0028. Find the Index of the First Occurrence in a String, this problem can use a hash function to reduce the time complexity of counting subsequences to O(1).
The time complexity is O(n), and the space complexity is O(n). Here, n is the length of the string s.
func findRepeatedDnaSequences(s string) []string {
hashCode := map[byte]int{'A': 0, 'C': 1, 'G': 2, 'T': 3}
ans, cnt, left, right := []string{}, map[int]int{}, 0, 0
sha, multi := 0, int(math.Pow(4, 9))
for ; right < len(s); right++ {
sha = sha*4 + hashCode[s[right]]
if right-left+1 < 10 {
continue
}
cnt[sha]++
if cnt[sha] == 2 {
ans = append(ans, s[left:right+1])
}
sha, left = sha-multi*hashCode[s[left]], left+1
}
return ans
}Complexity comparison
| Approach | Complexity |
|---|---|
| Sliding Window with Hash Map | Time Complexity: O(n), where n is the length of the string, due to the single pass over the string. |
| Bit Manipulation | Time Complexity: O(n), because each character is processed once. |
| Hash Table | – |
| Rabin-Karp String Matching Algorithm | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Sliding Window with Hash Map | O(n) | O(n) | Best for readability and quick implementation during interviews |
| Bit Manipulation Rolling Hash | O(n) | O(n) | When optimizing memory and demonstrating advanced hashing techniques |
Frequently asked questions
Is Repeated DNA Sequences easy or hard?
Repeated DNA Sequences is considered a medium difficulty problem. The basic sliding window and hashing solution is straightforward, but recognizing the bit manipulation optimization with 2-bit encoding requires deeper understanding of hashing and memory-efficient representations.
Repeated DNA Sequences Python/Java solution
Python and Java implementations usually rely on a sliding window and a hash map or set to store 10-character substrings. Each iteration extracts the substring, checks if it has appeared before, and records duplicates. The logic runs in linear time and is easy to implement in high-level languages.
How to solve Repeated DNA Sequences in O(n)?
Scan the string using a fixed-size sliding window of length 10. Store each sequence in a hash set or hash map and track duplicates. A more optimized version encodes each DNA character with 2 bits and maintains a rolling 20-bit hash, allowing constant-time updates while scanning the string once.
What is the best approach for Repeated DNA Sequences?
The most practical approach uses a sliding window of length 10 with a hash map to track seen substrings. Each window substring is inserted into the hash table and duplicates are recorded when the count reaches two. This method runs in O(n) time with O(n) space and is simple to explain in interviews.
Is Repeated DNA Sequences asked at Google/Amazon/Meta?
Repeated DNA Sequences is a common medium-level string and hashing problem seen in interviews at large tech companies. Variants involving substring hashing, rolling hashes, and duplicate detection appear in interviews at companies like Amazon and Google.
What data structure is used in Repeated DNA Sequences?
Hash tables or hash sets are the primary data structures used to track previously seen sequences. In optimized solutions, bit manipulation combined with a rolling hash encodes sequences into integers while still using a hash set for duplicate detection.
What is the time complexity of Repeated DNA Sequences?
The optimal solutions run in O(n) time where n is the length of the DNA string. Each position is processed once using a sliding window, and hash lookups are constant time on average. Space complexity is O(n) due to storing seen sequences or encoded hashes.
