滾動雜湊解決子串匹配問題
阿新 • • 發佈:2021-12-23
滾動雜湊
我們判斷兩個字串是否相同,往往是通過比較兩個字串的雜湊。
常用語言中計算字串雜湊的方法往往是一個字元一個字元的計算,導致計算字串雜湊的時間複雜度是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); } }