字符串 模式匹配

要點

模式匹配是數據結構中字符串的一種基本運算,給定一個子串,要求在某個字符串中找出與該子串相同的全部子串,這就是模式匹配html

假設P是給定的子串,T是待查找的字符串,要求從T中找出與P相同的全部子串,這個問題成爲模式匹配問題。P稱爲模式,T稱爲目標。若是T中存在一個或多個模式爲P的子串,就給出該子串在T中的位置,稱爲匹配成功;不然匹配失敗。

文中代碼是本人本身寫的,實測有效,含JAVA和C++兩種代碼。乾貨充足吧。
ios

 

蠻力算法 (BF算法)

蠻力算法(Brute-Force),簡稱BF算法。(男友算法,簡單粗暴—_—!)程序員

 

算法思想算法

BF算法的算法思想是: 數組

目標串T的的第一個字符起與模式串P的第一個字符比較。數據結構

若相等,則繼續對字符進行後續的比較;不然目標串從第二個字符起與模式串的第一個字符從新比較。ide

直至模式串中的每一個字符依次和目標串中的一個連續的字符序列相等爲止,此時稱爲匹配成功,不然匹配失敗。性能

 

經過下圖示例,可一目瞭然: spa

 

算法性能指針

假設模式串的長度是m,目標串的長度是n。

最壞的狀況是每遍比較都在最後出現不等,即沒變最多比較m次,最多比較n-m+1遍。

總的比較次數最多爲m(n-m+1),所以BF算法的時間複雜度爲O(mn)。

BF算法中存在回溯,這影響到效率,於是在實際應用中不多采用。

 

代碼

JAVA版本

 1  public  class BFMatch {
 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                 i++;
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++版本

 1 #include <iostream>
 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的子串前,遍歷整個target
11      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

 

 

相關閱讀

歡迎閱讀 程序員的內功——算法 系列

相關文章
相關標籤/搜索