數據結構- 串的模式匹配算法:BF和 KMP算法

Brute-Force算法的思想

1.BF(Brute-Force)算法  

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

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

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

Brute-Force算法的實現   

1.jpg

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 100  
  
typedef 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 =1;  
    while (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不要回溯,加快模式串的滑動速度。

2.jpg

需要討論兩個問題:
①如何由當前部分匹配結果確定模式向右滑動的新比較起點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):

3.jpg

由此,我們得到關系式:即得到到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)

4.jpg

有關系式: 即(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)’

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

5.jpg

由上,我們得到關系:

‘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的表達式。

1.jpg

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

7.jpg

模式匹配過程:

1.jpg

KMP算法的實現:

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

第二步:執行定位函數Index_kmp(與BF算法模塊非常相似)

  1. int KMPindex(SString S, SString T, int pos)  
    {  
        if (pos <1 ||  pos > S[0] ) exit(ERROR);  
        int i = pos, j =1;  
        while (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 100  
  
typedef 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 =1;  
    int 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] = 0;  
    while(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;  
}

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)  若Pk數據結構- 串的模式匹配算法:BF和 KMP算法Pj 則表明在模式串中:

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

      Pj-k+1 = P1, Pj-k+2 = P2,… Pj-1 = Pk-1.
     則當Pk數據結構- 串的模式匹配算法:BF和 KMP算法Pj時應將模式向右滑動至以模式中的第next[k]個字符和主串中的第 j 個字符相比較。
     若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 數據結構- 串的模式匹配算法:BF和 KMP算法Pk` ,則將模式繼續向右滑動直至將模式串中的第next[k`]個字符和Pj對齊,
     … ,一次類推,直至Pj和模式中某個字符匹配成功或者不存在k`(1< k` < j)滿足,則:
     next[j+1] =1;

    1.jpg


  1. /************************************************************************/  
    /*      求子串next[i]值的算法 
    */  
    /************************************************************************/  
    void GetNext(SString T, int next[])  
    {   int j = 1, k = 0;  
        next[1] = 0;  
        while(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]越大,模式串向右滑動得越遠,與主串進行比較的次數越少,時間復雜度就越低(時間效率)。

轉自:http://blog.csdn.net/hguisu/article/details/7676786

原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/2800

(0)
s19930811s19930811
上一篇 2015-04-07
下一篇 2015-04-07

相關推薦

  • 程序包管理yum

    YUM: YellowdogUpdate Modifier,rpm的前端程序,用來解決軟件包相關依賴性,可以在多個庫之間定位軟件包 存儲了眾多rpm包,以及包的相關的元數據文件(放置于特定目錄下:repodata) 文件服務器:     ftp://     http:// &n…

    Linux干貨 2016-08-23
  • 1.初識linux

    1.計算機組成 計算機五大部件:運算器、控制器、存儲器、輸入設備、輸出設備。對應實際情況大致如下: CPU CPU(Central Processing Unit)主要包含運算器、控制器;也包含寄存器、緩存用于輔助: CPU在同一時間能并行處理的二進制數的位數稱為CPU字長。字長由CPU對外數據通路的數據總線條數決定。 運算器 運算器也稱…

    Linux干貨 2016-08-22
  • CentOS 6.5 LVM磁盤管理學習筆記

    在系統運維和服務器管理過程中,經常遇到服務器磁盤容量不足,需要在線擴容的情況。普通磁盤分區的管理方式在邏輯分區劃好之后就無法改變其大小。而LVM可以實現Linux服務器下面磁盤空間的在線擴容和動態管理,相對于普通的磁盤分區有很大的靈活性。一、LVM簡介    LVM是 Logical VolumeManager(邏輯卷管理)的簡…

    Linux干貨 2016-08-29
  • gdisk用法

    gdisk用法   gdisk  – Interactive GUID partition table (GPT) manipulator GPT  fdisk (aka gdisk) is a text–mode menu–driven program for creation and ma…

    Linux干貨 2017-04-24
  • Linux下常用壓縮工具總結

    gzip: 1)只能針對普通文件進行壓縮,對文件夾、符號鏈接無效。 2)如果想對多個文件一起壓縮并打包,gzip是無法辦到的,需要結合tar進行 [root@mysql-master databackup]# ll 總用量 32 drwx—— 2 root root 4096&nb…

    Linux干貨 2016-12-05
  • 第一天

    今天講了很多

    Linux干貨 2018-03-26
欧美性久久久久