所有 DNA 都由一系列缩写为 ‘A’,’C’,’G’ 和 ‘T’ 的核苷酸组成,例如:”ACGAATTCCG”。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。

    编写一个函数来找出所有目标子串,目标子串的长度为 10,且在 DNA 字符串 s 中出现次数超过一次。

    示例 1:

    输入:s = “AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT”
    输出:[“AAAAACCCCC”,”CCCCCAAAAA”]
    示例 2:

    输入:s = “AAAAAAAAAAAAA”
    输出:[“AAAAAAAAAA”]

    提示:

    0 <= s.length <= 105
    s[i] 为 ‘A’、’C’、’G’ 或 ‘T’


    1. class Solution {
    2. /**
    3. 本题使用字符串哈希+哈希表的方式求解,先对s进行前缀和哈希
    4. 再遍历每个以i开始长度为10 的字符串哈希值,为了去重,我们之加入map中值为1的字符串
    5. */
    6. public List<String> findRepeatedDnaSequences(String s) {
    7. int n = s.length();
    8. //P一般取131或13331,取模Q一般是2^64,所以我们只用long就行,溢出就相当于取模了
    9. int P = 131;
    10. //h数组表示s的前缀和哈希,p数组时表示P进制
    11. long[] h = new long[n+10], p = new long[n+10];
    12. p[0] = 1;
    13. for (int i = 0; i < n; ++i) {
    14. p[i+1] = p[i] * P;
    15. h[i+1] = h[i] * P + s.charAt(i);
    16. }
    17. Map<Long,Integer> map = new HashMap<>();
    18. List<String> res = new ArrayList<>();
    19. for (int i = 0; i + 10 - 1 < n; ++i) {
    20. int j = i+10-1;
    21. //因为是从0开始遍历,h,p都是下标1开始的
    22. //hash = h[j] - h[i-1] * p[j-i+1];
    23. long hash = h[j+1] - h[i] * p[j-i+1];
    24. int count = map.getOrDefault(hash,0);
    25. //去重
    26. if (count == 1) res.add(s.substring(i,j+1));
    27. map.put(hash,count+1);
    28. }
    29. return res;
    30. }
    31. }