1. 程式人生 > >字串匹配之RK演算法——學習筆記

字串匹配之RK演算法——學習筆記

RK演算法是Rabin-Karp演算法的簡稱,是經典的字串匹配演算法,在《演算法導論》上是有介紹的,有興趣的同學可以去看看。

RK演算法的複雜度可以說是比上不足比下有餘,比一般的匹配演算法要好,但是又比不上KMP,Sunday等演算法。演算法表現跟快排比較相似,演算法平均複雜度表現較好,但最壞情況時複雜度會相對較高。

RK演算法的核心思想類似於hash函式。對於hash函式有了解的同學應該知道,我們通過hash函式可以將一個字串對映成一個數字(hash值)。當兩個字串的hash值不相同時,說明這兩個字串一定不匹配,而相同時則說明兩個字串是有可能匹配的,那麼就需要進一步驗證。

首先給兩個字串,假設str長度為n,pattern長度為m:

char str[]={"abcdefgdcbaefg"};
char pattern[]={"gdcbae"};

我們利用下面的公式可以用pattern計算出一個值X:

d=26;
L=P.length;
X=P[L-1]+d*(P[L-2]+d*(P[L-3]+...+(P[1]+d*P[0])));

把pattern帶入上面P中,可以求出一個值pattern_x,這個值就是代表pattern的hash值,或者說是對映值。d的取值根據字串字元的所有可能性值的數量來分配。比如數字0~9,此時d=10,又如字母a~z,此時d=26。

我們可以在str中找到 (n-m+1) 個長度與pattern相同的子串str_sub={str[i],,,str[i+m-1]},如下:

char str[]={"abcdefgdcbaefg"};

char st1[]={"abcdef"};
char st2[]={ "bcdefg"};
...
char st(n-m+1)[]={  "cbaefg"};

然後利用上面的公式就計算出這些子串的對映值,與pattern的對映值進行比較後,排除與pattern_x 不相同的對映值,留下相同的值,再取出子串和pattern之間進行字元比較,判斷是否匹配。

還有一個小問題,就是當pattern較長的時候,pattern_x會比較大,可能會超過最大長度範圍。這時只要取一個素數q,計算的時候對對映值取餘即可解決這個問題。q取素數會比較好,一般滿足q*d<INT_MAX即可。

好了,來看程式碼吧

//驗證兩個字串是否相同
bool isMatching(char *str,char *pattern) {
	for (int i = 0; pattern[i] != '\0'; i++) {
		if (pattern[i] != str[i]) {
			return false;
		}
	}
	return true;
}

//主函式
bool Fun(char *str, char *pattern) {
	int size1 = strlen(str);
	int size2 = strlen(pattern);

	int d = 26;
	int q = 144451;

	//str子串的對映值
	int s_code = str[0]-'a';    
	//pattern的對映值
	int p_code = pattern[0]-'a';
	//h為d的size2-1次冪
	int h=1;

	//計算 s_code、p_code、h
	for (int i = 1; i < size2; i++) {
		p_code = (d*p_code + pattern[i]-'a') % q;
		s_code = (d*s_code + str[i]-'a') % q;
		h = (h*d)%q;
	}

	//字串開始匹配,對p_code和s_code 進行比較,並更新s_code的值
	for (int i = 0; i < size1 - size2+1; i++) {
		if (s_code == p_code&&isMatching(str+i,pattern)) {
			return true;
		}

		//更新s_code,去掉開頭的值str[i],加上str[i+size2]
		s_code = ((s_code - h * (str[i] - 'a'))*d + str[i + size2] - 'a') % q;
	}
	return false;
}

更新str子串對映值可能會稍微難理解一些,舉個例子:

char str[]={"abcdefgdcbaefg"};
char st1[]={"abcdef"};
char st2[]={ "bcdefg"};

st1和st2中間的部分一致,僅首尾變化了,那麼可以推出:

st1_x-st1[0]*pow(d,m-1)%q=(st2_x-st2[m-1])/d;
st2_x=((st1_x-st1[0]*pow(d,m-1)%q)*d+st2[m-1])%q;

st1_x,st2_x為對映值。也就有了我上面程式碼中的公式:

s_code = ((s_code - h * (str[i] - 'a'))*d + str[i + size2] - 'a') % q;

好了,不早了,休息去了~~~~

相關推薦

字串匹配RK演算法——學習筆記

RK演算法是Rabin-Karp演算法的簡稱,是經典的字串匹配演算法,在《演算法導論》上是有介紹的,有興趣的同學可以去看看。 RK演算法的複雜度可以說是比上不足比下有餘,比一般的匹配演算法要好,但是又比不上KMP,Sunday等演算法。演算法表現跟快排比較相似,演算法平均複

字串匹配---BF演算法(暴力破解法)

        寫完第一篇字串匹配文章,發現竟然沒有介紹啥是字串匹配演算法,啥是KMP,直接就開講KMP的next陣列有點唐突。而在我打算寫第二篇的時候發現,我們為什麼要有KMP演算法,它到底比普通的演算法好在哪裡?回過頭來想想應該把普通的暴力法也寫寫,這樣才能明白它們的好

OpenCV學習筆記__特徵檢測與匹配 SURF演算法

SURF 演算法  ——“加速版的具有魯棒性的特徵”演算法 步驟: 特徵檢測 —— 特徵描述 —— 特徵匹配 實現流程: (1)特徵檢測:SurfFeatureDetector類 . detec

資料結構與演算法美專欄學習筆記-排序(上)

排序方法 氣泡排序、插入排序、選擇排序、快速排序、歸併排序、計數排序、基數排序、桶排序。   複雜度歸類 氣泡排序、插入排序、選擇排序 O(n^2) 快速排序、歸併排序 O(nlogn) 計數排序、基數排序、桶排序 O(n)     演算法的執行效率 1. 最

資料結構與演算法美專欄學習筆記-排序(下)

分治思想 分治思想 分治,顧明思意就是分而治之,將一個大問題分解成小的子問題來解決,小的子問題解決了,大問題也就解決了。 分治與遞迴的區別 分治演算法一般都用遞迴來實現的。分治是一種解決問題的處理思想,遞迴是一種程式設計技巧。   歸併排序 演算法原理 歸併的思想 先把陣列從中間分

資料結構與演算法學習筆記後進先出的“桶”

前言 棧最為一種的常用的資料結構,用“桶”來形容最合適不過;今天我們就來學習一下 正文 一、棧的定義? 1.“後進先出,先進後出”的資料結構。 2.從操作特性來看,是一種“操作受限”的線性表,只可以在一端插入和刪除資料。   二、為什麼需要棧?  

資料結構與演算法美專欄學習筆記-線性排序

線性排序 線性排序的概念 線性排序演算法包括桶排序、計數排序、基數排序。 線性排序演算法的時間複雜度為O(n)。 線性排序的特點 此3種排序演算法都不涉及元素之間的比較操作,是非基於比較的排序演算法。 對排序資料的要求很苛刻,重點掌握此3種排序演算法的適用場景。   桶排序 演算法

資料結構與演算法美專欄學習筆記-排序優化

選擇合適的排序演算法 回顧   選擇排序演算法的原則 1)線性排序時間複雜度很低但使用場景特殊,如果要寫一個通用排序函式,不能選擇線性排序。 2)為了兼顧任意規模資料的排序,一般會首選時間複雜度為O(nlogn)的排序演算法來實現排序函式。 3)同為O(nlogn)的快排和歸併排序相比,

資料結構與演算法美專欄學習筆記-陣列

什麼是陣列 陣列(Array)是一種線性表資料結構。它用一組連續的記憶體空間,來儲存一組具有相同型別的資料。 線性表 線性表就是資料排成像一條線一樣的結構。 常見的線性表結構:陣列,連結串列、佇列、棧等。 非線性表有:二叉樹、圖、堆等。 連續的記憶體空間和相同型別的資料 優點:兩限制使得

資料結構與演算法美專欄學習筆記-複雜度分析

複雜度分析 什麼是複雜度分析 資料結構和演算法解決是“如何讓計算機更快時間、更省空間的解決問題”。 因此需從執行時間和佔用空間兩個維度來評估資料結構和演算法的效能。 分別用時間複雜度和空間複雜度兩個概念來描述效能問題,二者統稱為複雜度。 複雜度描述的是演算法執行時間(或佔用空間)與資料規模的增長關係

資料結構與演算法美專欄學習筆記-二分查詢(下)

四種常見的二分查詢變形問題 查詢第一個值等於給定值的元素 //查詢第一個等於給定值的元素 public static int BSearch2(int[] a, int n, int value){ //定義陣列頭尾索引 int low = 0, high = n - 1;

資料結構與演算法美專欄學習筆記-跳錶

跳錶的概念 對連結串列建立n級索引,例如每兩個結點提取一個節點到上一層,稱之為索引層。 圖中的down表示down指標,指向下一級結點   跳錶的時間複雜度 跳錶的高度 跳錶的高度是log2n。 跳錶的時間複雜度 跳錶中查詢某個資料的時間複雜度是O(logn)。  

資料結構與演算法美專欄學習筆記-雜湊演算法

雜湊演算法的定義和原理 將任意長度的二進位制串對映為固定長度的二進位制串。 這個對映的規則就是雜湊演算法,而通過原始資料對映之後得到的二進位制串就是雜湊值。 設計一個優秀的雜湊演算法需要滿足: 從雜湊值不能反向推匯出原始資料(所以雜湊演算法也叫單向雜湊演算法); 對輸入資料非常敏感,哪怕原始

資料結構與演算法美專欄學習筆記-二叉樹基礎(上)

樹 節點的定義 樹中的元素稱之為節點 高度的定義 節點的高度:節點到葉子節點的最長路徑 樹的高度:跟節點的高度 深度的定義 根節點到這個節點所經歷的邊的個數 層的定義 節點的深度+1   二叉樹 滿二叉樹 除了葉子結點外每個節點都有左右兩個子節點 完全二叉樹 葉子結

資料結構與演算法美專欄學習筆記-二叉樹基礎(下)

二叉查詢樹 Binary Search Tree  二叉查詢樹的定義 二叉查詢樹又稱二叉搜尋樹。其要求在二叉樹中的任意一個節點,其左子樹中的每個節點的值,都要小於這個節點的值,而右子樹的節點的值都大於這個節點的值。 二叉查詢樹的查詢操作 二叉樹類、節點類以及查詢方法的程式碼實現

演算法學習筆記——priority queue、heapsort、symbol table、binary search trees

Priority Queue 類似一個Queue,但是按照priority的大小順序來出隊 一般存在兩種方式來實施 排序法(ordered),在元素入隊時即進行排序,這樣插入操作為O(N),但出隊為O(1) 不排序法(unordered),元素直接插入到後面,出隊時先排序後提取,插入操作為O(1),出隊為O

資料結構與演算法學習筆記 複雜度分析

前言:   大家都知道資料結構和英語,就如同程式設計師的兩條腿一樣;只有不斷的積累,學習,擁有了健壯的“雙腿”才能越走越遠;在資料結構和演算法的領域,不得不承認自己就是一隻菜鳥;需要不斷的學習;在學習過程中,經常會有一些自己的看法,和別人獨特的見解;我都會一一做好筆記,以便進步; 正文:複雜度分析

資料結構與演算法學習筆記 提高讀取效能的連結串列(上)

前言   連結串列(Linked list)比陣列稍微複雜一點,在我們生活中用到最常見的應該是快取,它是一種提高資料讀取效能的技術,常見的如cpu快取,瀏覽器快取,資料庫快取等。今天我們就來學習一下連結串列 正文 一、連結串列的定義? 1.一種線性表(資料排成像一條線一樣的結構。每個線性表上的資料最多

資料結構與演算法學習筆記先進先出的佇列

前言   佇列是一種非常實用的資料結構,類似於生活中發排隊,可應用於生活,開發中各個方面,比如共享印表機(先請求先列印),訊息佇列。你想知道他們是怎麼工作的麼。那就來一起學習一下佇列吧 正文 一、佇列的定義? 1.一種先進先出的線性表 2.只允許入棧 push()和出棧 pop() 在後端(稱

資料結構與演算法學習筆記高效、簡潔的編碼技巧“遞迴”

前言 盜夢空間想象大多數人都看過:電影講述的是主人公諾蘭進入希裡安·墨菲夢境植入想法的行動。為了向希裡安·墨菲夢植入理念,影片進入四層夢境,即所謂:“夢中的夢中 夢中人的夢中”。 有一對兔子,每隔三個月會產下一對小兔子,小免子每隔三個月,也會產生新的一對免子,問36個月後,共有多少對兔子。 諸如此類:其