【字符串】字符串查找 ( Rabin-Karp 算法 )
文章目錄
- 一、字符串查找
- 二、Rabin-Karp 算法
一、字符串查找
算法題目鏈接 : https://www.lintcode.com/problem/13/
在 一個字符串 中查找 另外一個字符串 第一次出現的位置 ;
如 : 在 “abcdefghijk” 中查找 “def” 第一次出現的位置 , 是 444 ;
該方法使用 暴力算法 , 兩層 for 循環 , 肯定可以解決 ; 如果用暴力算法 , 那面試基本就涼了 ; 暴力算法的復雜度是 O(m×n)O(m \times n)O(m×n) , mmm 是第一個大字符串的長度 , nnn 是被查找的字符串長度 ;
KMP 算法 是專門用于解決該問題的算法 , 該算法 只能用于解決在一個字符串中查找另外一個字符串的問題 ; KMP 算法主要靠背誦 , 沒有涉及到算法的理論 , 只能用于解決單一字符串查找問題 , 一般面試時不考慮使用該算法 ; KMP 算法的算法復雜度是 O(m+n)O(m + n)O(m+n) ;
Rabin-Karp 算法 比 KMP 算法更簡單 , 其基本原理就是比較字符串的 哈希碼 ( HashCode ) , 快速的確定子字符串是否等于被查找的字符串 ;
二、Rabin-Karp 算法
假設要在 “abcde” 字符串中 , 尋找字符串 “cde” ;
遍歷時 , 如果使用蠻力算法遍歷 , 先對比 “abc” 是否與 “cde” 相等 , 明顯不等 ,
繼續遍歷 , 向右平移一位 , 對比 “bcd” 與 “cde” 是否相等 ;
這里從 “abc” 平移到 “bcd” , 如果使用整個字符串比較的話 , 假如字符串的位數是 nnn 位 , 則復雜度是 O(n)O(n)O(n) , 這里如果能將復雜度變為 O(1)O(1)O(1) , 那么時間復雜度將大大降低 ;
兩個 nnn 位的字符串比較 , 那么需要逐位對比 , 時間復雜度是 O(n)O(n)O(n) , 這里使用哈希函數 , 對比兩個字符串的哈希值 , 這樣將時間復雜度降低為 O(1)O(1)O(1) ;
哈希函數 :
哈希函數可以將任何類型的數據 , 字符串 , 整型 , 字節等數據 , 轉為整數 ;
哈希表是一個很大的數組 , 使用哈希函數 , 將某個字符串對應到哈希表中某個位置上 , 相同的字符串使用哈希函數計算的整數結果是相同的 ;
靜置轉換哈希函數 , 是最常用的哈希函數 ;
如 : “abcde” 的哈希碼值為 a×314+b×313+c×312+d×311+e×310a \times 31^4 + b \times 31^3 + c \times 31^2 + d \times 31^1 + e \times 31^0a×314+b×313+c×312+d×311+e×310
313131 是一個魔法值 , 使用該值效率最高 , 一般都設置這個數 ; 整個公式類似于組合數學中的生成函數 ;
這個結果很大 , 可能超過整數表示范圍 , 為該值模一個較大的數 , 模的數越大 , 沖突的概率就越小 ;
(a×314+b×313+c×312+d×311+e×310)mod106(a \times 31^4 + b \times 31^3 + c \times 31^2 + d \times 31^1 + e \times 31^0) \mod 10^6(a×314+b×313+c×312+d×311+e×310)mod106
哈希表計算 :
計算 “abcde” 的子字符串哈希值 ,
先計算 “abc” 的哈希值 xxx ,
然后計算 “abcd” 的哈希值 (x×31+d)mod106(x \times 31 + d) \mod 10^6(x×31+d)mod106 , 然后將 “a” 的哈希值減掉 , (x×31+d?a×313)mod106(x \times 31 + d - a \times 31^3) \mod 10^6(x×31+d?a×313)mod106 ;
這樣就可以在 O(1)O(1)O(1) 的時間內 , 得到 “abc” 的哈希值 , 然后在 O(1)O(1)O(1) 的事件內得到 “bcd” 的哈希值 ;
被查找的字符串 “cde” 的哈希值是不變的 , 可以在開始計算出來 ;
這里注意 , 哈希值相等 , 并不是代表字符串完全相等 , 理論上講 , 有可能存在哈希值相等 , 字符串不相等的時候 , 雖然概率及其微小 , 建議在哈希值相等的情況下 , 再次判定一次字符串是否相等 ;
哈希碼不同 , 則字符串一定不同 ;
哈希碼相同 , 字符串不一定相同 ;
遍歷 mmm 次 , 用于遍歷外層字符串索引 , 哈希值計算復雜度為 O(1)O(1)O(1) ,
那么 整體復雜度是 O(m)O(m)O(m) ,
只有在哈希值相等的時候 , 才遍歷 nnn 個子字符串 , 復雜度是 O(n)O(n)O(n) ,
那么該算法的 整體時間復雜度是 O(m+n)O(m + n)O(m+n) ;
class Solution {/*** @param source:* @param target:* @return: return the index*/public int strStr(String source, String target) {if (target == null || source == null) {// 不符合規則, 直接返回 -1return -1;}// 計算哈希碼int base = 1000000;int m = target.length();if (m == 0) {return 0;}// 計算 31^mint power = 1;for (int i = 0; i < m; i++) {power = (power * 31) % base;}// 計算被查找字符串哈希值int targetCode = 0;for (int i = 0; i < m; i++) {targetCode = (targetCode * 31 + target.charAt(i)) % base;}// 循環主體int hashCode = 0;for (int i = 0; i < source.length(); i++) {// 注意遍歷的 i 用于計算哈希值// 哈希值代表的字符串的起始位置是 i - m + 1hashCode = (hashCode * 31 + source.charAt(i)) % base;// 如果不足 m 個字符, 不執行后續操作if (i < m - 1) {continue;}// 大于 m 個字符需要減去首位的哈希值if (i > m - 1) {hashCode = hashCode - (power * source.charAt(i - m)) % base;// 確保相減的結果是正數if (hashCode < 0) {hashCode += base;}}if (hashCode == targetCode) {// 雙重驗證if (source.substring(i - m + 1, i + 1).equals(target)) {return i - m + 1;}}}return -1;} }class Main {public static void main(String[] args) {int index = new Solution().strStr("mabcban", "cb");System.out.println(index);} }
總結
以上是生活随笔為你收集整理的【字符串】字符串查找 ( Rabin-Karp 算法 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【字符串】字符串查找 ( 蛮力算法 )
- 下一篇: 【算法】双指针算法 ( 双指针算法分类