1. 程式人生 > >數據結構學習之字符串匹配算法(BF||KMP)

數據結構學習之字符串匹配算法(BF||KMP)

優秀代碼 esp 數組 得到 最大 寫法 知識 好玩 lar

數據結構學習之字符串匹配算法(BF||KMP)

0x1 實驗目的

? 通過實驗深入了解字符串常用的匹配算法(BF暴力匹配、KMP、優化KMP算法)思想。

0x2 實驗要求

? 編寫出BF暴力匹配、KMP、優化KMP的代碼模型

0x2 代碼

0x2.1.1 BF暴力匹配

#include <iostream>
#include <string>

using namespace std;
int BF1(string s1,string s2)
{
    int len=s2.length();
    for(int i=0;i<s1.length();i++)
    {
        int n=len;
        int j=i;
        while(n)
        {
            //cout<< s1[j] <<endl;
            if(s1[j++] != s2[len-n])
                break;
            n--;
        }
        if(n==0)
            return i+1;
    }
    return -1;
}

int BF2(string s1,string s2)
{
    int i=0;
    int j=0;
    while(i<s1.length() && j<s2.length())
    {
        if(s1[i]==s2[j])
        {
            i++;
            j++;
        }else
        {
            i=i-j+1;
            j=0;
        }
    }
    if(j>=s2.length())
        return (i-j+1);
    else
        return -1;
}

int main()
{
    string s1="asfasgasgsd";
    string s2="asg";
    cout<< BF1(s1,s2) <<endl;
    cout<< BF2(s1,s2) <<endl;
    return 0;
}

0x2.1.2 結果

? 技術分享圖片

0x2.1.3 體會

? 我寫了兩種,一開始我沒看書自己意淫了第一種出來,代碼不夠書本簡潔,本著向優秀代碼學習的精神,還有對應對應下面KMP的匹配過程,第二種寫法更有益於學習。過程主要是,while(i<s.length() && j<t.length())來判斷退出,其中跟kmp不同的是,i需要i-j+1,j=0回溯,該算法的時間復雜度0(n*m)。

0x2.2 KMP || KMP優化

#include <iostream>
#include <string>
#define maxsize 200+7
using namespace std;
int next[maxsize];
int nextval[maxsize];

void GetNext(string s,int next[])
{
    int j=0,k=-1;
    next[0]=-1;
    while(j<s.length()-1) //因為數組下標最大是s1.length()-1,下面是j++故j可以到達最大值
    {
        if(k==-1 || s[j]==s[k])
        {
            j++,k++;
            next[j]=k;
        }else
        {
            k=next[k];
        }
    }
}

void GetNextVal(string s,int nextval[])
{
    int j=0,k=-1;
    nextval[0]=-1;
    while(j<s.length()-1)
    {
        if(k==-1 || s[j]==s[k])
        {
            j++,k++;
            if(s[j]!=s[k])
                nextval[j]=k;
            else
                nextval[j]=nextval[k];
        }else
        {
            k=nextval[k];
        }
    }
}

//KMP優化
int KMPIndex1(string s,string t)
{
    int i=0,j=0;
    int next[maxsize];
    GetNext(t,next);
    while(i<s.length() && j<t.length())
    {
        if(j==-1 || s[i]==t[j])
        {
            i++,j++;
        }else
        {
            j=next[j];
        }
    }
    if(j>=t.length())
        return(i-t.length());
    else
        return -1;

}
//KMP優化
int KMPIndex2(string s,string t)
{
    int i=0,j=0;
    int nextval[maxsize];
    GetNextVal(t,nextval);
    while(i<s.length() && j<t.length())
    {
        if(j==-1 || s[i]==t[j])
        {
            i++,j++;
        }else
        {
            j=nextval[j];
        }
    }
    if(j>=t.length())
        return(i-t.length());
    else
        return -1;

}
int main()
{
    string s="aaaaab";
    string t="aaab";
    cout<< KMPIndex1(s,t) <<endl;
    cout<< KMPIndex2(s,t) <<endl;
    return 0;
}

0x2.2.1 結果

? 技術分享圖片

0x2.2.2 體會

? kmp算法主要思想是利用模式串自身的特點,避免主串的回溯過程,同時通過next數組,也減少了模式串的回溯長度。

首先是定義:
\[ next[j]=\left\{\begin{matrix}-1 \qquad 當j=0時 \\MAX \left \{ k|0<k<j\ 且 \ t_{0}t_{1}\cdots t_{k-1} = t_{j-k}t_{j-k+1}\cdots t_{j-1}\right \} \ 當此集合非空時 \\ 0 \qquad 其他情況 \end{matrix}\right. \]
主要思想是:

? 比如一個模式串 ababb

顯然前4個字符串滿足 ab=ab 也就是\[t_{0}t_{1}=t_{2}t_{3}\] 當你去用模式串去匹配串ababaababb的時候可以發現\[t_{4} \neq s_{4}\],這個時候就直接可以跳轉到 \[s_{2}\] 開始 而不是從\[s_{1}\]開始,關於這個證明其實也很簡單,如下:

? aba \[\neq\] bab 然後你發現這裏了沒有,從\[s_{1}\]開始其實就是從bab開始很明顯就是不等,其實kmp就是這種規律,找出最大長度的前後綴,那麽就確定了這個模式串滑動的長度,說的更簡單點就是模式串包含了主串的信息,模式串跟主串的比較就可以轉換為模式串跟自己的比較,就像上面的例子,通過反證法可以得到移動<next[j]的話必定會不想等。

代碼重點是:

//初始化
next[0]=-1;
int j=0,k=-1;
while(j<s.length()-1)
{
  if(k==-1 || s[j]==s[k])
  {
    j++,k++;
    next[j]=k; //重點
  }else
  {
    k=next[k]; //重點 abdbabc 顯然c -> ab開頭的兩個字符,d不等於c,那麽只能從a=c這裏去比較了next[2]=0
  }
    
}

0x3 最近學習總結

最近感覺自己特別浪,要學的東西還有很多,平時效率也好低,數據結構的作業也拖了好久,以前都是4天一次,這次竟然拖了那麽10多天,最近要抓緊空閑時間去補回來了,下篇寫一些遞歸的題目,介紹一些好玩的知識點。

數據結構學習之字符串匹配算法(BF||KMP)