1. 程式人生 > 其它 >滾動雜湊解決子串匹配問題

滾動雜湊解決子串匹配問題

滾動雜湊

我們判斷兩個字串是否相同,往往是通過比較兩個字串的雜湊。

常用語言中計算字串雜湊的方法往往是一個字元一個字元的計算,導致計算字串雜湊的時間複雜度是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);

    }
}
我有一壺酒 足以慰風塵 盡傾江海里 贈飲天下人