Victor Zhang的專欄
模式匹配是資料結構中字串的一種基本運算,給定一個子串,要求在某個字串中找出與該子串相同的所有子串,這就是模式匹配。
假設P是給定的子串,T是待查詢的字串,要求從T中找出與P相同的所有子串,這個問題成為模式匹配問題。P稱為模式,T稱為目標。如果T中存在一個或多個模式為P的子串,就給出該子串在T中的位置,稱為匹配成功;否則匹配失敗。文中程式碼是本人自己寫的,實測有效,含JAVA和C++兩種程式碼。乾貨充足吧。
蠻力演算法(Brute-Force),簡稱BF演算法。(男朋友演算法,簡單粗暴—_—!)
演算法思想
BF演算法的演算法思想是:
從目標串T的的第一個字元起與模式串P
若相等,則繼續對字元進行後續的比較;否則目標串從第二個字元起與模式串的第一個字元重新比較。
直至模式串中的每個字元依次和目標串中的一個連續的字元序列相等為止,此時稱為匹配成功,否則匹配失敗。
通過下圖示例,可一目瞭然:
演算法效能
假設模式串的長度是m,目標串的長度是n。
最壞的情況是每遍比較都在最後出現不等,即沒變最多比較m次,最多比較n-m+1遍。
總的比較次數最多為m(n-m+1),因此BF演算法的時間複雜度為O(mn)。
BF演算法中存在回溯,這影響到效率,因而在實際應用中很少採用。
程式碼
JAVA版本
2
3 static int bfMatch(String target, String pattern) {
4 int pos = -1;
5 int i = 0, j = 0, k = 0;
6
7 // 在沒找到匹配pattern的子串前,遍歷整個target 8 while (-1 == pos && i < target.length()) {
9
10 // 將目標串和模式串逐一比對,如果有不同的則退出11 while (j < pattern.length() && target.charAt(i) == pattern.charAt(j)) {
12
13 j++;
14 }
15
16 if (j >= pattern.length()) { // 如果模式串掃描完,說明目標串中含有這個子串17 pos = k;
18 } else { // 反之,沒有掃描完,則從目標串的下一個字元開始重新逐一比對19 j = 0;
20 k++;
21 i = k;
22 }
23 }
24
25 return pos;
26 }
27
28 public static void print(String target, String pattern, int index) {
29 if (-1 != index) {
30 System.out.format("[%s] is in the Pos = %d of [%s]\n", pattern, index, target);
31 } else {
32 System.out.format("[%s] is not in the [%s]\n", pattern, target);
33 }
34 }
35
36 public static void main(String[] args) {
37 String target = "Hello World";
38 String pattern = "llo";
39 String pattern2 = "Woe";
40
41 int index = bfMatch(target, pattern);
42 int index2 = bfMatch(target, pattern2);
43 print(target, pattern, index);
44 print(target, pattern2, index2);
45
46 }
47
48 } BF演算法之JAVA實現
C++版本
2 #include <string>
3
4 using namespace std;
5
6 int bfMatch(string target, string pattern) {
7 int pos = -1;
8 int i = 0, j = 0, k = 0;
9
10 // 在沒找到匹配pattern的子串前,遍歷整個target11 while (-1 == pos && i < (int)target.length()) {
12
13 // 將目標串和模式串逐一比對,如果有不同的則退出14 while (j < (int)pattern.length() && target[i] == pattern[j]) {
15 i++;
16 j++;
17 }
18
19 if (j >= (int)pattern.length()) { // 如果模式串掃描完,說明目標串中含有這個子串20 pos = k;
21 } else { // 反之,沒有掃描完,則從目標串的下一個字元開始重新逐一比對22 j = 0;
23 k++;
24 i = k;
25 }
26 }
27
28 return pos;
29 }
30
31 void print(string target, string pattern, int index) {
32 if (-1 != index) {
33 cout << "[" << pattern << "] is in the Pos = " << index << " of [" << target << "]" << endl;
34 } else {
35 cout << "[" << pattern << "] is not in the [" << target << "]" << endl;
36 }
37 }
38
39 int main()
40 {
41 string target = "Hello World";
42 string pattern = "llo";
43 string pattern2 = "Woe";
44
45 int index = bfMatch(target, pattern);
46 int index2 = bfMatch(target, pattern2);
47 print(target, pattern, index);
48 print(target, pattern2, index2);
49 return 0;
50 } BF演算法之C++實現
執行結果
[llo] is in the Pos = 2 of [Hello World][Woe] is not in the [Hello World] KMP演算法
Knuth-Morris-Pratt演算法(簡稱KMP),是由D.E.Knuth、J.H.Morris和V.R.Pratt共同提出的一個改進演算法,消除了BF演算法中回溯問題,完成串的模式匹配。
演算法思想
在BF演算法中,用模式串去和目標串的某個子串比較時,如果不全部匹配,就要回溯到起始位置,然後後移。顯然,移回到前面已經比較過的位置,還是不能完全匹配。
KMP演算法的思想是,設法利用這個已知資訊,跳過前面已經比較過的位置,繼續把它向後移,這樣就提高了效率。
由此可知,KMP演算法其實有兩大要點:
(1) 計算跳轉位置資訊,這裡我們稱之為部分匹配表。
(2) 後移到指定位置,重新開始匹配。
首先,來看如何獲得部分匹配表。
為了確定匹配不成功時,下次匹配時 j的位置,引入了next[]陣列,next[j]的值表示模式串P[0...j-1]中最長字尾的長度等於相同字元序列的字首。這個next 陣列叫做部分匹配表。
對於next[]陣列的定義如下:
對於BF演算法中的例子,模式串P=“abcac”,根劇next[j]的定義,可得到下表:
j | 0 | 1 | 2 | 3 | 4 |
t[j] | a | b | c | a | c |
next[j] | -1 | 0 | 0 | 0 | 1 |
有了部分匹配表,就可以後移到指定位置
在匹配過程中,若發生不匹配的情況。如果next[j] >= 0,則目標串的指標 i 不變,將模式串的指標 j 移動到 next[j] 的位置繼續進行匹配;
若next[j] = -1,則將 i 右移1位,並將 j 置0,繼續進行比較。
以上要點配合下面的示意圖理解,效果會更好哦。
演算法效能
假設模式串的長度是m,目標串的長度是n。在KMP演算法中求next陣列的時間複雜度為O(m),在後面的匹配中因目標串T的下標不用回溯,所以比較次數可記為n。
由此,得出KMP演算法的總的時間複雜度為O(n+m)。
程式碼
JAVA版本
1 public class KMPMatch {2
3 // 計算部分匹配表 4 public static int[] getNext(String pattern) {
5 int j = 0, k = -1;
6 int[] next = new int[pattern.length()];
7 next[0] = -1;
8 while (j < pattern.length() - 1) {
9 if (-1 == k || pattern.charAt(j) == pattern.charAt(k)) {
10 j++;
11 k++;
12 next[j] = k;
13 } else {
14 k = next[k];
15 }
16 }
17
18 return next;
19 }
20
21 // KMP演算法22 static int kmpMatch(String target, String pattern) {
23 int i = 0, j = 0, index = 0;
24 int[] next = getNext(pattern); // 計算部分匹配表25
26 while (i < target.length() && j < pattern.length()) {
27 if (-1 == j || target.charAt(i) == pattern.charAt(j)) {
28 i++;
29 j++;
30 } else {
31 j = next[j]; // 如果出現部分不匹配,獲取跳過的位置32 }
33 }
34
35 if (j >= pattern.length())
36 index = i - pattern.length(); // 匹配成功,返回匹配子串的首字元下標37 else
38 index = -1; // 匹配失敗39
40 return index;
41
42 }
43
44 // 列印完整序列45 public static void printAll(int[] list) {
46 for (int value : list) {
47 System.out.print(value + "\t");
48 }
49 System.out.println();
50 }
51
52 public static void main(String[] args) {
53 String target = "ababcabcacbab";
54 String pattern = "abcac";
55 int index = kmpMatch(target, pattern);
56 System.out.format("[%s] is in the pos = %d of [%s]", pattern, index, target);
57 }
58
59 } KMP演算法之JAVA實現 C++版本 1 #include <iostream>
2 #include <string>
3
4 using namespace std;
5
6 const int MAX = 100;
7 int next[MAX] = {0};
8
9 // 計算部分匹配表10 void getNext(string pattern) {
11 int j = 0, k = -1;
12 next[0] = -1;
13 while (j < (int)pattern.length() - 1) {
14 if (-1 == k || pattern[j] == pattern[k]) {
15 j++;
16 k++;
17 next[j] = k;
18 } else {
19 k = next[k];
20 }
21 }
22 return;
23 }
24
25 // KMP演算法26 int kmpMatch(string target, string pattern) {
27 int i = 0, j = 0, index = 0;
28 getNext(pattern); // 計算部分匹配表29
30 while (i < (int)target.length() && j < (int)pattern.length()) {
31 if (-1 == j || target[i] == pattern[j]) {
32 i++;
33 j++;
34 } else {
35 j = next[j]; // 如果出現部分不匹配,獲取跳過的位置36 }
37 }
38
39 if (j >= (int)pattern.length())
40 index = i - pattern.length(); // 匹配成功,返回匹配子串的首字元下標41 else
42 index = -1; // 匹配失敗43
44 return index;
45
46 }
47
48 void print(string target, string pattern, int index) {
49 if (-1 != index) {
50 cout << "[" << pattern << "] is in the Pos = " << index << " of [" << target << "]" << endl;
51 } else {
52 cout << "[" << pattern << "] is not in the [" << target << "]" << endl;
53 }
54 }
55
56 int main()
57 {
58 string target = "ababcabcacbab";
59 string pattern = "abcac";
60 int index = kmpMatch(target, pattern);
61 print(target, pattern, index);
62 return 0;
63 } KMP演算法之C++實現
執行結果
[abcac] is in the pos = 5 of [ababcabcacbab] 參考資料 《資料結構習題與解析》(B級第3版)http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html
http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html
相關推薦
Victor Zhang的專欄
要點 模式匹配是資料結構中字串的一種基本運算,給定一個子串,要求在某個字串中找出與該子串相同的所有子串,這就是模式匹配。 假設P是給定的子串,T是待查詢的字串,要求從T中找出與P相同的所有子串,這個問題成為模式匹配問題。P稱為模式,T稱為目標。如果T中存在一個或多個模式為P
【Rachel Zhang的專欄】因為理想,所以拼搏,關注計算機應用的點點滴滴……
百度自然語言處理部RD,關注機器翻譯,計算機視覺,機器學習,演算法研究,人工智慧, 移動網際網路等學科和產業,希望結識更多同道中人。 新浪微博:Rachel____Zhang <!--公眾號:-->
Rachel Zhang的專欄(因為理想,所以拼搏,關注計算機應用的點點滴滴……)
百度自然語言處理部RD,關注機器翻譯,計算機視覺,機器學習,演算法研究,人工智慧, 移動網際網路等學科和產業,希望結識更多同道中人。 新浪微博:Rachel____Zhang <!--公眾號:-->
Rachel Zhang的專欄
This is a simple game.The goal of the game is to roll two balls to two holes each.'B' -- ball'H' -- hole'.' -- land'*' -- wallRemember when a ball rolls in
Arvon Zhang的專欄
知識不是智慧,解決問題的方法才是智慧。 知識不是力量,智慧才是真正的力量。 “教育就是叫人去思維” ,思考力比記憶力更為重要和有效。 身體不好是廢品,思想不好是毒品。 人品,是人真正的最高學歷, 人品,是人能力施展的基礎, 是當今社會稀缺而珍貴的品質。 還想再多看幾篇可能感
張林林|深藍(Linlin Zhang,shenlan211314) 的專欄
原文地址:http://www.gljpkc.com/jsjkxdl/jxwz/ynjx/1.htm“歐拉回路”與“哈密爾頓迴路”1.歐拉回路17世紀的東普魯士有一座哥尼斯堡(Konigsberg)城(現為俄國的加里寧格勒(Kaliningrad)城),城中有一座奈佛夫(Kneiphof)島,普雷格爾(Pre
愛留圖 - 一個定期開設專欄活動的圖片收集網站誕生。
數據庫 解決方案 sql 本章和大家分享的是一個自制的圖片收集網站:愛留圖;本章不打算分享什麽技術知識點,而分享的主要內容有網站的創立的需求,現階段采用的技術架構,服務器配置等信息;在站點服務構建時用到的部分技術,知識點,以及遇到的問題會在後面分不同的章節+不同的小節來講解,樂於和大家分享自己的經
Victor 串口控件 1.5.0.6 VCL/FMX for C++ Builder 10.2 Tokyo, 10.1 Berlin, 10.0 Seattle, XE8, XE7, XE6 已經發布
blank sms mac 使用 模板 www 文本 clas stat Victor 串口控件 1.5.0.6 更新內容: ? 增加支持 FMX (Firemonkey) Win32/Win64,控件包含 VCL 和 FMX 兩個框架版本的,可以同時安裝 ? 增加
發布 Victor 串口控件 1.5.0.6 VCL for C++ Builder 6.0
引腳 amp ans dem mode 發的 結構 base 設計思路 Victor 串口控件 1.5.0.6 VCL BCB6/BCB5 版本更新的內容: ? 和新版 BCB 的控件同步更新,BCB6 版本和新版 C++ Builder 控件只是 UNICODE/AN
HDOJ 5421 Victor and String 回文串自己主動機
end with data- 插入 art else ack 自己 != 假設沒有操作1,就是裸的回文串自己主動機...... 能夠從頭部插入字符的回文串自己主動機,維護兩個last點就好了..... 當整個串都是回文串的時候把兩個last統一一下 Vict
java執行程序的內存分析系列專欄
運行時 nbsp oid main 空間 如圖所示 jvm 性別 class 近段時間研究了java的程序執行過程中的內存分配,收獲頗多,解決了我最近時間學習java的很多困惑點。寫java內存分析系列的目的主要有兩個,一來是希望給像我一樣的java初學者
HDOJ 5418 Victor and World 狀壓DP
ice sel queue cti -- courier adding ber bestcoder 水狀壓DP Victor and World Time Limit: 4000/2000 MS (Java/Others) Memory L
Collections of Zujin Zhang's Published works
top gradient dimens lease near mmu diagonal horizon electron I am not good, but I shall do my best to be better. Please click https:
李開復華盛頓郵報專欄:發錢解決AI失業潮
2-2 都在 替代 客戶 人在 ews 狀況 職業 屬於 昨天,創新工場創始人兼 CEO 李開復博士在《華盛頓郵報》撰文稱,人工智能革命即將到來,這可能是一個最好的時代,也可能是最壞的時代。 好與壞,將取決於我們如何解決人工智能的副作用之一——失業問題。 根據牛津
【專家專欄】淺談百度搜索排序
百度搜索排序站長圈經常聊的話題中,怎麽提升百度排序一定是排名TOP3的問題,那百度排序的原理是什麽,該怎麽提升,今天給大家分享一下經驗心得。關於排序這件事兒對於像百度搜索來說,並沒有排序這一說法,搜索引擎認為排序是在特定的關鍵詞下網站內容的位置,而關鍵詞是由用戶搜索產生,如果一個關鍵詞沒有被搜索,也就意味著這
(轉載)【笨木頭Lua專欄】基礎補充20:面向對象——類和繼承
笑話 ava span 生成 code BE 手機 情況 忽略 終於來了,在Lua中的面向對象編程,相信目前學習Lua的大部分人都是為了開發手機網遊吧。而且基本都是奔著腳本語言的熱更新特性去的,所以全腳本開發變得十分流行。 對於普及不太廣的Lua(相對於C++、Java等主
(轉載)【笨木頭Lua專欄】基礎補充22:弱引用table
ive AC -c 所在 lan contain 函數 貢獻 缺陷 這次要介紹的內容比較少,就一個——弱引用table 笨木頭花心貢獻,哈?花心?不,是用心~ 轉載請註明,原文地址:http://www.benmutou.com/archives/1808 文章來源:
(轉載)【笨木頭Lua專欄】基礎補充21:面向對象——多重繼承、私密性
子類 先來 nta 參數 hive lua 封裝 完成 存在 在Lua中的多重繼承和私密性可能用得比較少,也可能只是我個人用得比較少。 本來想偷懶不寫這文章的,因為我今天剛買了個漂移板,連起步都還沒學會啊,想多學一會。 咳咳,本著堅持不懈、負責到底的態度,我還是決定隨便寫幾
SQLmap攻防實戰技術專欄開通
SQLmap攻防實戰技術專欄開通以前做專題研究,覺得專題研究才有意義,因為專題更加系統,更加科學,更加條理化,這次應51cto網站的邀請,準備了一個攻防實戰的專欄:滲透攻擊入門到實踐鏈接地址:http://blog.51cto.com/cloumn/detail/3目前該專欄已經正式上線,第一次做專欄,有些地
完成sqlmap滲透攻擊入門到實戰專欄文章初步目標
sqlmap滲透攻擊《滲透攻擊入門到實戰》專欄文章http://blog.51cto.com/cloumn/detail/3從推出到目前基本預訂目標17章內容完成更新。回顧整個過程有以下一些感受:1.技術的東西必須深入,目前安全圈安全生態不是特別好,很多內容都是copy,筆者在對某些內容進行查看時,根據文章提