1. 程式人生 > >《數據結構》學習筆記3——串匹配

《數據結構》學習筆記3——串匹配

圖1 cnblogs style 進制 count 記錄 uil ref www

/*.. 前言:感謝學堂在線的學習資源!!!感謝鄧公!!本文代碼均整理自課件 ..*/

通常,字符種類不多,而串長>>字符種類數量。

% grep <pattern> <text> 定義:模式P 文本T

Pattern matching: detection? location? counting? enumeration?(本文主要討論出現位置)

數據結構,借助C++中<cstring>頭文件:

技術分享圖片技術分享圖片

詳情參考 http://www.cplusplus.com/reference/cstring/ http://www.cplusplus.com/reference/string/string/

算法性能評估:隨機T,對成功、失敗的匹配分別測試(成功,在T中,隨機取出長度為m的子串作為P,分析平均復雜度;失敗,采用隨機P,統計平均復雜度)

蠻力算法:O(m*n)

技術分享圖片
 1 int match_BruteForce(char * P, char * T) {
 2     size_t n = strlen(T), i = 0;
 3     size_t m = strlen(P), j = 0;
 4     for (i = 0; i <= n - m; ++i) {
 5         for (j = 0; j < m; ++j)
 6             if (T[i + j] != P[j]) break
; 7 if (m <= j) break; // 找到匹配子串 8 } 9 return (i > n - m ) ? -1 : i; // 返回-1表示匹配失敗。 10 }
int match_BruteForce(char * P, char * T)

KMP算法(Kunth Morris Pratt三位大家):O(m+n)

相比蠻力算法優化:P快速右移,避免重復比對(利用成功匹配的經驗,構造next表)。

【下圖1體會KMP比對過程,優化前的next表】【圖2 next表構造思路】【圖3自繪優化後的next表構造程序流程圖,啊哈哈哈】

技術分享圖片技術分享圖片技術分享圖片

當使用蠻力算法,單次匹配概率越小時(比如P中為較少出現的字符),此時最好情況接近O(n),KMP相比蠻力算法優勢不明顯。而例如二進制串匹配,則KMP算法性能優勢明顯。

技術分享圖片
 1 int match_KMP(char * P, char * T) {
 2     int * next = buildNext(P);
 3     int n = (int)strlen(T), i = 0;
 4     int m = (int)strlen(P), j = 0;
 5     while (j < m && i < n)
 6         if (j < 0 || T[i] == P[j]) { // 匹配
 7             i++; j++;
 8         }
 9         else  // 失敗
10             j = next[j]; 
11     delete[] next;
12     // return i - j; // 返回
13     return (i - j > n - m) ? -1 : i; // 返回-1表示匹配失敗。
14 }
int match_KMP(char * P, char * T)

BM_BC算法 [ Boyer + Moore, 1997] :最好O(n/m),最差O(n*m) (壞字符 Bad Character – 失敗教訓)

相比KMP算法,由於越靠後的位置,作用越大,因此對模式串P從後向前匹配。(利用匹配失敗的經驗)

構建bc表:記錄全字符在匹配串中的位置,在匹配失敗時【右】移動至匹配的位置,使得當前匹配成功。

技術分享圖片

優點:單次匹配概率越小時,性能優勢越明顯(大字母表,特別是Unicode);P越長,移動效果越明顯。

缺點:單次匹配概率越大的場合,性能越接近蠻力算法(小字母表,DNA)。

技術分享圖片
1 int * buildBC(char * P) {
2      int * bc = new int[256]; //bc表,與字母表等長
3      for (size_t j = 0; j < 256; ++j) bc[j] = -1; // 此初始化可省略
4      for (size_t m = strlen(P), j = 0; j < m; j++)
5          bc[P[j]] = j; //不斷覆蓋P[j]的出現位置
6      return bc;
7 }
int * buildBC(char * P)

BM_GS算法 :兼顧BM_BC算法和KMP算法的思路(好後綴 Good Suffix – 成功經驗)

【下圖體會一下同時考慮匹配好後綴,和壞字符的策略】

技術分享圖片

性能比較:BM_GS算法最壞O(n/m),最好O(m+n)

技術分享圖片

Karp-Rabin 算法:串即是數!(這是一種思想!)

算法:散列+ 相鄰串的位運算O(1)

利用散列(模余函數)對串進行篩選,再進一步確認是否匹配;每一次篩選,即從上一個串的散列值到下一個串的散列值,計算只需要O(1)時間。

《數據結構》學習筆記3——串匹配