1. 程式人生 > >資料結構- 串的模式匹配演算法 BF和 KMP演算法

資料結構- 串的模式匹配演算法 BF和 KMP演算法

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

Brute-Force演算法的思想

1.BF(Brute-Force)演算法  

Brute-Force演算法的基本思想是:

1) 從目標串s 的第一個字元起和模式串t的第一個字元進行比較,若相等,則繼續逐個比較後續字元,否則從串s 的第二個字元起再重新和串t進行比較。

2) 依此類推,直至串t 中的每個字元依次和串s的一個連續的字元序列相等,則稱模式匹配成功,此時串t的第一個字元在串s 中的位置就是t 在s中的位置,否則模式匹配不成功。

Brute-Force演算法的實現   


c語言實現:

// Test.cpp : Defines the entry point for the console application.  //  #include "stdafx.h"  #include <stdio.h>  #include "stdlib.h"#include
<iostream>
using namespace std;//巨集定義    #define TRUE   1    #define FALSE   0    #define OK    1    #define ERROR   0  #define  MAXSTRLEN 100typedef char SString[MAXSTRLEN + 1];/************************************************************************/
/*  返回子串T在主串S中第pos位置之後的位置,若不存在,返回0*//************************************************************************/int BFindex(SString S, SString T, int pos)if (pos <1 ||  pos > S[0] ) exit(ERROR); int i = pos, j =1while (i<= S[0] && j <= T[0]) {  if (S[i] == T[j])  {   ++i; ++j;  } else {   i = i- j+ 2;   j = 1;  } } if(j > T[0]) return i - T[0]; return ERROR;}void main(){ SString S = {13,'a','b','a','b','c','a','b','c','a','c','b','a','b'}; SString T = {5,'a','b','c','a','c'}; int pos; pos = BFindex( S,  T, 1); cout<<"Pos:"<<pos;}


2.KMP演算法

2.1 演算法思想:

每當一趟匹配過程中出現字元比較不等時,不需要回溯I指標,而是利用已經的帶的“部分匹配”的結果將模式向右滑動儘可能遠的一段距離後,繼續進行比較。

即儘量利用已經部分匹配的結果資訊,儘量讓i不要回溯,加快模式串的滑動速度。





需要討論兩個問題:
①如何由當前部分匹配結果確定模式向右滑動的新比較起點k?
② 模式應該向右滑多遠才是高效率的?

現在討論一般情況:

假設 主串:s: ‘s(1)  s(2) s(3) ……s(n)’ ;  模式串 :p: ‘p(1)  p(2) p(3)…..p(m)’

現在我們假設 主串第i個字元與模式串的第j(j<=m)個字元失配後,主串第i個字元與模式串的第k(k<j)個字元繼續比較

此時,s(i)≠p(j):


由此,我們得到關係式:即得到到1 到  j -1 "部分匹配"結果:

 ‘P(1)  P(2) P(3)…..P(j-1)’   =    ’ S(i-j+1)……S(i-1)’

 從而推匯出k 到 j- 1位的“部分匹配”:即Pj-1j-k=S前i-1~i- (k -1))位             

  ‘P(j - k + 1) …..P(j-1)’  =  ’S(i-k+1)S(i-k+2)……S(i-1)’

由於s(i)≠p(j),接下來s(i)將與p(k)繼續比較,則模式串中的前(k-1)個字元的子串必須滿足下列關係式,並且不可能存在  k’>k  滿足下列關係式:(k<j)


有關係式: 即(P的前k- 1 ~ 1位= S前i-1~i-(k-1) )位 ) ,

‘P(1) P(2)  P(3)…..P(k-1)’ = ’S(i-k+1)S(i-k+2)……S(i-1)’

現在我們把前面總結的關係綜合一下,有:


由上,我們得到關係:

‘p(1)  p(2)  p(3)…..p(k-1)’  =   ‘p(j - k + 1) …..p(j-1)’ 

      反之,若模式串中滿足該等式的兩個子串,則當匹配過程中,主串中的第i 個字元與模式中的第j個字元等時,僅需要將模式向右滑動至模式中的第k個字元和主串中的第i個字元對齊。此時,模式中頭k-1個字元的子串 ‘p(1)  p(2)  p(3)…..p(k-1)’  必定與主串中的第i 個字元之前長度為k-1 的子串   ’s(j-k+1)s(j-k+2)……s(j-1)’相等,由此,匹配僅需要從模式中的第 k 個字元與主串中的第 i 個字元比較起 繼續進行。      若令 next[j] = k ,則next[j] 表明當模式中第j個字元與主串中相應字元“失配”時,在模式中需要重新和主串中該字元進行的比較的位置。由此可引出模式串的next函式:

根據模式串P的規律:  ‘p(1)  p(2)  p(3)…..p(k-1)’  =   ‘p(j - k + 1) …..p(j-1)’ 

由當前失配位置j(已知) ,可以歸納計算新起點k的表達式。





由此定義可推出下列模式串next函式值:




模式匹配過程:


KMP演算法的實現:

第一步,先把模式T所有可能的失配點j所對應的next[j]計算出來;

第二步:執行定位函式Index_kmp(與BF演算法模組非常相似

int KMPindex(SString S, SString T, int pos)if (pos <1 ||  pos > S[0] ) exit(ERROR); int i = pos, j =1while (i<= S[0] && j <= T[0]) {  if (S[i] == T[j]) {   ++i; ++j;  } else {   j = next[j+1];  } } if(j > T[0]) return i - T[0]; return ERROR;}


完整實現程式碼:

// Test.cpp : Defines the entry point for the console application.  //  #include "stdafx.h"  #include <stdio.h>  #include "stdlib.h"#include <iostream>using namespace std;//巨集定義    #define TRUE   1    #define FALSE   0    #define OK    1    #define ERROR   0  #define  MAXSTRLEN 100typedef char SString[MAXSTRLEN + 1];void GetNext(SString T, int next[]);int KMPindex(SString S, SString T, int pos);/************************************************************************//*  返回子串T在主串S中第pos位置之後的位置,若不存在,返回0*//************************************************************************/int KMPindex(SString S, SString T, int pos)if (pos <1 ||  pos > S[0] ) exit(ERROR); int i = pos, j =1int next[MAXSTRLEN]; GetNext( T, next); while (i<= S[0] && j <= T[0]) {  if (S[i] == T[j]) {   ++i; ++j;  } else {   j = next[j];  } } if(j > T[0]) return i - T[0]; return ERROR;}/************************************************************************//*      求子串next[i]值的演算法*//************************************************************************/void GetNext(SString T, int next[]){   int j = 1, k = 0; next[1] = 0while(j < T[0]){  if(k == 0 || T[j]==T[k]) {      ++j;  ++k;  next[j] = k;    } else {   k = next[k];   } }}void main(){ SString S = {13,'a','b','a','b','c','a','b','c','a','c','b','a','b'}; SString T = {5,'a','b','c','a','c'}; int pos; pos = KMPindex( S,  T, 1); cout<<"Pos:"<<pos;}



2.2  求串的模式值next[n]

k值僅取決於模式串本身而與相匹配的主串無關。

我們使用遞推到方式求next函式:
1)由定義可知:
     next[1] = 0;
2)  設 next[j] = k ,這個表面在模式串中存在下列關係:
    ‘P(1)  ….. P(k-1)’  =   ‘P(j - k + 1) ….. P(j-1)’ 
    其中k為滿足1< k <j的某個值,並且不可能存在k` > 滿足:
    ‘P(1)  ….. P(k`-1)’  =   ‘P(j - k` + 1) ….. P(j-1)’ 
    此時next[j+1] = ?可能有兩種情況:
   (1) 若Pk = Pj,則表明在模式串中:

  ‘P(1) ….. P(k)’  =   ‘P(j - k + 1) ….. P(j)’ 
          並且不可能存在k` > 滿足:  ‘P(1) ….. P(k`)’  =   ‘P(j - k` + 1) ….. P(j)’ 
          即next[j+1] = k + 1 推到=》:

         next[j+1] = next[j] + 1;

      (2)  若PkPj 則表明在模式串中:

          ‘P(1) ….. P(k)’     ‘P(j - k + 1) ….. P(j)’ 
     此時可把next函式值的問題看成是一個模式匹配的問題,整個模式串即是主串又是模式串
     而當前匹配的過程中,已有:

      Pj-k+1 = P1, Pj-k+2 = P2,... Pj-1 = Pk-1.
     則當PkPj時應將模式向右滑動至以模式中的第next[k]個字元和主串中的第 個字元相比較。
     若next[k] = k`,且Pj= Pk`, 則說明在主串中的第j+1 個字元之前存在一個長度為k` (即next[k])的最長子串,和模式串
     從首字元其長度為看k`的子串箱等。即
       ‘P(1) ….. P(k`)’  =  ‘P(j - k` + 1) ….. P(j)’ 
     也就是說next[j+1] = k` +1
     next[j+1] = next[k] + 1
     同理,若
Pj Pk` ,則將模式繼續向右滑動直至將模式串中的第next[k`]個字元和Pj對齊,
     ... ,一次類推,直至Pj和模式中某個字元匹配成功或者不存在k`(1< k` < j)滿足,則:
     next[j+1] =1;

    


/************************************************************************//*      求子串next[i]值的演算法*//************************************************************************/void GetNext(SString T, int next[]){   int j = 1, k = 0; next[1] = 0while(j < T[0]){  if(k == 0 || T[j]==T[k]) {      ++j;  ++k;  next[j] = k;    } else {   k = next[k];   } }}

next  函式值究竟是什麼含義,前面說過一些,這裡總結。 設在字串 S 中查詢模式串 T ,若 S[m]!=T[n], 那麼,取 T[n] 的模式函式值 next[n], 1.         next[n] = 0  表示 S[m] T[1] 間接比較過了,不相等,下一次比較  S[m+1]  T[1] 2.         next[n] =1  表示比較過程中產生了不相等,下一次比較  S[m]  T[1] 3.         next[n] = k >1  k<n,  表示 ,S[m] 的前 k 個字元與 T 中的開始 k 個字元已經間接比較相等了,下一次比較 S[m] T[k] 相等嗎? 4.         其他值,不可能。

注意:

(1)k值僅取決於模式串本身而與相匹配的主串無關。

(2)k值為模式串從頭向後及從j向前的兩部分的最大相同子串的長度

(3)這裡的兩部分子串可以有部分重疊的字元,但不可以全部重疊。

next[j]函式表徵著模式P中最大相同字首子串和字尾子串(真子串)的長度。

可見,模式中相似部分越多,則next[j]函式越大,它既表示模式T字元之間的相關度越高,也表示j位置以前與主串部分匹配的字元數越多。

即:next[j]越大,模式串向右滑動得越遠,與主串進行比較的次數越少,時間複雜度就越低(時間效率)。



           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述