滚动哈希解决子串匹配问题

滚动哈希

我们判断两个字符串是否相同,往往是通过比较两个字符串的哈希。

常用语言中计算字符串哈希的方法往往是一个字符一个字符的计算,导致计算字符串哈希的时间复杂度是O(C),其中C是字符串的长度。

如果我们要计算字符串长度为C的子串的哈希,时间复杂度为O(NC)。

我们可以利用前缀的思想,采用滚动哈希。

滚动哈希就是一种O(1)时间复杂度的计算子串哈希的方法。

重复的DNA序列

//一种直观的思路,遍历所有可能存在的子串,然后判断子串出现了几次(算哈希)
//需要注意的是,传统对字符串求哈希的过程,实际是O(C)复杂度,C是字符串的长度
//因此我们可以使用滚动哈希把这一过程变为O(1)
class Solution {
    public List<String> findRepeatedDnaSequences(String s) {
        int n = s.length();
        //P是一个质数。一般我们先尝试13,再尝试131313,来避免哈希冲突
        int P=131313;
        List<String> ans = new ArrayList<>();
        int[] p=new int[n+1];
        //h[k]表示前k-1个字符的哈希值
        int[] h=new int[n+1];
        p[0] = 1;
        for (int i = 1; i<=n; i++) {
           h[i]=h[i-1]*P+s.charAt(i-1);
           p[i]=p[i-1]*P;
        }
        Map<Integer, Integer> map = new HashMap<>();
        for(int i=1;i+10-1<=n;i++){
            int j=i+10-1;
            //这里✖p[10],10是子串的长度
            //至于为啥是这样,建议举个例子自己算算
            int hash=h[j]-h[i-1]*p[10];
            int cnt = map.getOrDefault(hash, 0);
            if (cnt == 1) ans.add(s.substring(i - 1, i + 10 - 1));
            map.put(hash, cnt + 1);
        }
        return ans;
    }
}

实现strstr()

//滚动哈希也能解决
class Solution {
    public int strStr(String ss, String pp) {
        int n=ss.length();
        int m=pp.length();
        int P=131313;
        long hash=0;

        //先算我们的目标哈希
        for(int i=0;i<m;i++){
            hash=hash*P+pp.charAt(i);
        }
        long[] p=new long[n+1];
        long[] h=new long[n+1];
        p[0]=1;
        for(int i=1;i<=n;i++){
            h[i]=h[i-1]*P+ss.charAt(i-1);
            p[i]=p[i-1]*P;
        }
        for(int i=1;i+m-1<=n;i++){
            int j=i+m-1;
            long cur=h[j]-h[i-1]*p[m];
            if(cur==hash){
                return i-1;
            }
        }
        return -1;

      
    }
}

检查一个字符串是否包含所有长度为k的二进制子串

//滚动哈希很好用!
class Solution {
    public boolean hasAllCodes(String s, int k) {
        int n=s.length();
        long[] p=new long[n+1];
        long[] h=new long[n+1];
        int P=13;
        p[0]=1;
        for(int i=1;i<=n;i++){
            h[i]=h[i-1]*P+s.charAt(i-1);
            p[i]=p[i-1]*P;
        }
        HashSet<Long> set=new HashSet<>();
        for(int i=1;i+k-1<=n;i++){
            long hash=h[i+k-1]-h[i-1]*p[k];
            set.add(hash);
        }
        return set.size()==Math.pow(2,k);

    }
}
posted @ 2021-12-23 15:07  刚刚好。  阅读(281)  评论(0编辑  收藏  举报