面試演算法之字串匹配演算法,Rabin-Karp演算法詳解
既然談論到字串相關演算法,那麼字串匹配是根本繞不過去的坎。在面試中,面試官可能會要你寫或談談字串的匹配演算法,也就是給定兩個字串,s 和 t, s是要查詢的字串,t是被查詢的文字,要求你給出一個演算法,找到s在t中第一次出現的位置,假定s為 acd, t為acfgacdem, 那麼s在t中第一次出現的位置就是4.
字串匹配演算法有多種,每種都有它的優點和缺陷,沒有哪一種演算法是完美無缺的。如果在面試中被問到這個問題,最好的處理方法是先詳細的給出一個具體演算法,然後再去大概的探討其他方法的優劣,做到這一點,通過的勝算就相當大了,由此,我們需要了解主流的字串匹配演算法。我們先總結一下常用匹配演算法的特點:
演算法 | 預處理時間 | 匹配時間 |
---|---|---|
暴力匹配法 | O(m) | O((n-m+1)m) |
Rabin-Karp | O(m) | O((n-m+1)m |
Finite automaton | O(m | |
O(n) |
Knuth-Morris-Pratt | O(m) | O(n) |
上標中,m是匹配字串s的長度,n是被匹配文字t的長度,符號
同時符號 | | 表示文字長度,例如s=”abcd”, 那麼|s| 就等於4.所以,如果
大家如果對字串匹配演算法比較瞭解的話,一定對KMP演算法早有耳聞,雖然它在理論上的時間複雜度是最好的,但這並不意味著,它就是最好的演算法,因為它實現起來比較複雜,因此在大多數情況下,其他演算法往往優於KMP,並且在很多運用情形下,其他演算法的執行效率未必比KMP差多少。
我們需要定義幾個概念:
1. 字首,如果字串w是x的字首,那意味著x可以分解成兩個字串的組合, x = wy, 例如 w=ab, x =abcd, 於是x可以分解成w=ab , y = cd兩個字串的組合,如果w是x的字首,那麼|x| <= |w|
2. 字尾,如果字串w是x的字尾,那就是x可以分解成兩個字串的組合,x=yw, 而且有 |w| <= |x|
還有一個簡單的定理:
有三個字串x, y ,z. 如果x, y 都是z 的字首,那麼當|x| < |y| 時,x是y的字首,如果|x|>|y| ,那麼y是x的字首。如果x,y是z的字尾,那麼也同理可證。
如果字串P含有m個字元,記為P[1…m], 那麼P的長度為k的字首P[1…k], 記做
上面的定義有點燒腦,大家可以拿筆畫畫,以便於理解。
暴力列舉法
列舉法的流程是,在範圍[0, n-m] 中,查詢是否存在一個值s, 0<=s<=n-m, 使得 P[1…m] = T[s+1, … , s+m].java程式碼如下:
int match(String p, String t) {
for (int s = 0; s < t.length() - p.length(); s++) {
for (int i = 0; i < p.length(); i++) {
if (p.charAt(i) == t.charAt(s+i)
&& i == p.length() - 1) {
return s;
} else if(p.charAt(i) != t.charAt(s+i)){
break;
}
}
}
return -1;
}
如果t = “acaabc”, p = “aab”, 那麼上面演算法執行流程如下:
a c a a b c
a a b
a c a a b c
a a b
a c a a b c
a a b (成功匹配)
程式碼中,最壞情況下,外層的for迴圈次數為m - n + 1 次,內層for迴圈執行 m 次,所以列舉法的演算法複雜度是O((m-n+1) m)。列舉法的效率很低,因為在匹配過程中,它完全忽略了P的自身組成特點。後面的演算法之所以效率得以提高,很大程度上,就是重複考慮了P自身的字元組合特點。
Rabin-Karp演算法
該演算法在實際運用中,表現不錯,RK演算法需要O(m) 時間做預處理,在最壞情況下,該演算法的複雜度與列舉法一樣都是,O((n - m + 1) m).但在實際運用中,最壞情況極少出現。
假設字符集全是由0到9的數字組成,
把數字從字串轉換為對應的整形數值,我們前頭講過,時間複雜度為O(m).通過下面的公式進行轉換即可:
p = P[m] + 10(P[m-1] + 10(P[m-2]+ …. + ( 10(P[2]) + P[1])…).
RK演算法有一個巧妙之處在於如何通過
T=”314152”, 那麼可以算出
由此可以得到通用公式:
由於一次計算的複雜度是O(1), 計算
雖說我們當前處理的是數字字串,如果處理的文字是小寫字元{a,b…z}, 其實本質是一樣的,只要把十進位制的數值{0,1..9}換成26進位制的數字{0, 1, 2, ….25},那面公式中的10換成26即可。
上面演算法,含有一個問題,那就是當m過大,於是對應的數值p或
其中, h
然而引入求餘又會引發新的問題,
這就解釋了為何RK演算法最壞情況下,複雜度會是O(m (n - m + 1)). 因為有可能出現這樣情況,
舉個具體例子看看:
T = “2359023141526739921”, q = 13, d = 10, P=31415,m=5,不難發現s = 6 的時候,滿足T[s+1, … s+m+1] = P[1..5], 但是當s=12時,T[s+1, …, s+m+1] = “67399”, 然而7
下面我們看看java實現程式碼:
public class RabinKarp {
private String T ;
private String P ;
private int d;
private int q;
private int n = 0;
private int m = 0;
private int h = 1;
//假設要匹配的文字字符集是小寫字母{a...z}
public RabinKarp(String t, String p, int d, int q) {
T = t;
P = p;
this.d = d;
this.q = q;
n = T.length();
m = P.length();
for (int i = 0; i < m-1 ; i++) {
h *= (d );
h = (h % q);
}
}
public int match() {
int p = 0;
int t = 0;
//預處理,計算p 和 t0.
for (int i = 0; i < m; i++) {
p = (d*p + (P.charAt(i) - 'a')) % q;
t = (d*t + (T.charAt(i) - 'a')) % q;
}
for (int s = 0; s <= n - m; s++) {
if (p == t) {
//如果求餘後相等,那麼就逐個字元比較
for (int i = 0; i < m; i++) {
if (i == m-1 && P.charAt(i) == T.charAt(s+i)) {
return s;
} else if (P.charAt(i) != T.charAt(s+i)){
break;
}
}
}
if (s <= n - m) {
t = (d*(t-(T.charAt(s) - 'a')*h) + T.charAt(s+m) - 'a') % q;
if (t < 0) {
t += q;
}
}
}
return -1;
}
}
public class ArrayAndString {
public static void main(String[] args) {
String T = "abcabaabcabac";
String P = "abaa";
RabinKarp rk = new RabinKarp(T, P, 26, 29);
int s = rk.match();
System.out.println("Valid shift is: "+ s);
}
}
在match 呼叫中,一開始就先計算p 和
上面的程式碼執行後,輸出結果s 等於3, 也就是T[3,..6] = P[1..4].執行結果顯示程式碼實現,基本正確。
後面的兩種演算法,將在後續章節中,我們再深入研究。
相關推薦
面試演算法之字串匹配演算法,Rabin-Karp演算法詳解
既然談論到字串相關演算法,那麼字串匹配是根本繞不過去的坎。在面試中,面試官可能會要你寫或談談字串的匹配演算法,也就是給定兩個字串,s 和 t, s是要查詢的字串,t是被查詢的文字,要求你給出一個演算法,找到s在t中第一次出現的位置,假定s為 acd, t為a
隱馬爾科夫演算法之實現簡易版的拼音輸入法程式碼詳解
這段時間瞭解了隱馬爾科夫演算法,然後拼音輸入法的核心就是HMM,然後從github上找了一個輸入法實現的程式碼來更透徹的理解演算法,本文程式碼來源:https://github.com/LiuRoy/Pinyin_Demo,如果侵權,請聯絡我刪除!!! 一、 拼音輸入法的原理概述 1.主要原
高效面試之字串匹配(KMP,AC演算法)
3.AC 多模匹配演算法 看下面這個例子:給定5個單詞:say she shr he her,然後給定一個字串yasherhs。問一共有多少單詞在這個字串中出現過。 三步:構建trik樹,給trik樹新增失敗路徑,建立AC自動機,根據AC自動機搜尋文字 1.構建trik樹 1constint ki
Rabin-karp演算法實現 字串匹配
// RabinKarp演算法實現 // RabinKarp演算法實現 const primeRK = 16777619 func hashStr(seq string) (uint32, uint32) { hash := uint32(0) for _, value
演算法題之字串匹配問題
我最近複習一道困難程度的演算法題,發現了許多有趣之處。在借鑑了他人解法後,發現從最簡單的情況反推到原題是一種解鎖新進階的感覺。從遞迴到動態規劃,思維上一步一步遞進,如同一部跌宕起伏的小說,記錄下來和諸君共賞之。 題目如下: 給你一個字串 s 和一個字元規律 p,請你來實現一個支援
C++ Leetcode初級演算法之字串中的第一個唯一字元
給定一個字串,找到它的第一個不重複的字元,並返回它的索引。如果不存在,則返回 -1。 案例: s = “leetcode” 返回 0. s = “loveleetcode”, 返回 2. 注意事項:您可以假定該字串只包含小寫字母。 class Solution { pub
貪心演算法之+-字串
思路:兩個字串第i個‘-’的距離 (i從1到字串的‘-’的個數),然後加和即可。(思路還是說不出來,看程式碼) #include <std
PS圖層混合演算法之四(亮光, 點光, 線性光, 實色混合)
亮光模式: 根據繪圖色通過增加或降低“對比度”,加深或減淡顏色。如果繪圖色比50%的灰亮,影象通過降低對比度被照亮,如果繪圖色比50%的灰暗,影象通過增加對比度變暗。 線性光模式:根據繪圖色通過增
KMP演算法(字串匹配演算法)
KMP演算法主要是要計算匹配字元的字首表(prefix table), 舉例:如下面字串的字首表就是陰影框框中的部分。利用字首表來進行匹配 例子:(匹配字元)p=ABABCABAA, (待匹配字元)t=ABABABABCABAAB 具體主要就是求字首表,然後將
ACM經典演算法之字串處理
一、(字串替換) 語法:replace(char str[],char key[],char swap[]); 引數: str[]: 在此源字串進行替換操作 key[]: 被替換的字串,不能為空串 swap[]: 替
PS圖層混合演算法之五(飽和度,色相,顏色,亮度)
飽和度模式: HcScYc =HBSAYB 飽和度模式:是採用底色的亮度、色相以及繪圖色的飽和度來建立最終色。如果繪圖色的飽和度為0,則原圖沒有變化。 輸出影象的飽和度為上層,色調和亮度保持為下層。
基礎演算法之字串轉整數(Leetcode-8)
春招第一步,演算法伴我行 計劃著每天研究幾道演算法題,各個型別儘可能都包含,自己寫出來,好做增強。基本都使用python語言編寫,主要講一下自己的思路,以及AC情況。水平不夠,大家多指正,不吝賜教,十分感謝。 想起之前頭條面試的一道演算法題(另一道下次說),字串轉整數,之前有做過,但是面
《機器學習實戰》學習筆記(四)之Logistic(上)基礎理論及演算法推導、線性迴歸,梯度下降演算法
轉載請註明作者和出處:http://blog.csdn.net/john_bh/ 執行平臺: Windows Python版本: Python3.6 IDE: Sublime text3 一、概述 Logistic迴歸是統計學習中的經典
【演算法分析】字串匹配:BF、KMP演算法
字串匹配演算法:BF、KMP演算法程式碼。 /***************************************** Copyright (c) 2015 Jingshuang Hu @filename:demo.c @datetime:20
字串匹配(BF,BM,Sunday,KMP演算法解析)
字串匹配一直是計算機領域熱門的研究問題之一,多種演算法層出不窮。字串匹配演算法有著很強的實用價值,應用於資訊搜尋,拼寫檢查,生物資訊學等多個領域。 今天介紹幾種比較有名的演算法: 1. BF 2. BM 3. Sunday 4. K
快速字串匹配一: 看毛片演算法(KMP)
前言 由於需要做一個快速匹配敏感關鍵詞的服務,為了提供一個高效,準確,低能耗的關鍵詞匹配服務,我進行了漫長的探索。這裡把過程記錄成系列部落格,供大家參考。 在一開始,接收到快速敏感詞匹配時,我就想到了 KMP 翻譯過來叫“看毛片“的演算法,因為大學的時候就學過它。聽說到它的效率非常高。把原本字串匹配效率 O(
排程演算法之MCT(Minimum Completion Time)演算法
最小完成時間演算法MCT(Minimum CompletionTime)是以任意的順序將任務對映到具有最早完成時間的主機上,它並不保證任務被指派到執行它最快的主機上,而僅關心如何最小化任務完成時間,因而可能導致任務在資源上的執行時間過長,從而潛在地增加了排程跨度。 public void run
資料結構與演算法之美專欄學習筆記-雜湊演算法
雜湊演算法的定義和原理 將任意長度的二進位制串對映為固定長度的二進位制串。 這個對映的規則就是雜湊演算法,而通過原始資料對映之後得到的二進位制串就是雜湊值。 設計一個優秀的雜湊演算法需要滿足: 從雜湊值不能反向推匯出原始資料(所以雜湊演算法也叫單向雜湊演算法); 對輸入資料非常敏感,哪怕原始
網遊同步演算法之導航推測(Dead Reckoning)演算法
在瞭解該演算法前,我們先來談談該演算法的一些背景資料。大家都知道,在網路傳輸的時候,延遲現象是很普遍的,而在基於Server/Client結構下的網路遊戲的同步也就成了很頭疼的問題,在保證客戶端響應使用者本地指令流暢的情況下,沒法有效的保證的同步的及時性。同樣,在軍方也有類似
【Java進階面試系列之三】哥們,訊息中介軟體在你們專案裡是如何落地的?【石杉的架構筆記】
歡迎關注個人公眾號:石杉的架構筆記(ID:shishan100) 週一至週五早8點半!精品技術文章準時送上! 一、前情回顧 之前給大家聊了一下,面試時如果遇到訊息中介軟體這個話題,面試官上來可能問的兩個問題: 你們的系統架構中為什麼要引入訊息中介軟體? 系統架構中引入訊息中介軟體有什麼缺點? 關於