KMP算法詳解

lidki 9年前發布 | 18K 次閱讀 算法

字符串模式匹配我們相信大家都有遇過,然而我們也習慣用簡單匹配法(即Brute-Force算法),其基本思路就是一個個逐一對比下去,這也是我們大家熟知的方法,然而這種算法的效率并不高,但利于理解。

假設主串s="ababcabcacbab",模式串為t="abcac",我們用肉眼很容易看出匹配位置為是s[5]--s[10];利用簡單匹配算法代碼如下:

int BF(string s,string t)//Brute-Force,簡單匹配算法
{
    int origin=-1;//模式匹配的起始位置
    for(int i=0;i<s.size();i++)
    {
        int k;
        for(k=0;k<t.size();k++)
        {
            if(s[i+k]!=t[k])
               break;
        }
        if(k==t.size())//匹配成功
        {
            origin=i;
            break;
        }
    }
    return origin;//返回匹配成功的位置,若為-1,則匹配失敗
}

然后這種算法效率顯而易見效率并不高,其中包含了過多的不必要操作,并不能很好地達到實際工作中需要的效率。這種算法在最好的情況下時間復雜度為O(m),在最壞的情況下時間復雜度為M(nm)。

KMP算法

KMP算法較Brute-Force算法有較大的改進,主要消除了主串指針的回溯,從而是算法的效率有了某種程度的提高。

為了消除主串的指針回溯,需要分析模式串t,我們利用next[]數組來記錄存放這些“部分匹配信息”。

next[]計算規則如下:

1)next[j]=-1; j=0;

2) next[j]=max(k); 0<k<j,p[0....k-1]=p[j-k...j-1];

3) next[j]=0; 其他條件

例如:計算ababa的next[]數組值

按照上面的規則我們可以填寫出下表的next[]數組

p a b a b a
j 0 1 2 3 4
next[j] -1 0 0 1 2

根據上面規則,當j=0,next[j]=-1,所以第一個a的next值為-1;

當j=1時,0<k<j這樣的k不存在,所以屬于其他條件,即為0;

當j=4時,0<k<j,這樣的k=1,2,3,因為要求最大的K,即從大到小驗證k是否滿足p[0....k-1]=p[j-k...j-1],若一個都不滿足,屬于其他條件,即為0,

當k=3時,str1=p[0....k-1]=aba,str2=p[j-k...j-1]=bab,str1!=str2,所以k!=3;

當k=2時,str1=p[0....k-1]=ab,str2=p[j-k...j-1]=ab,str1==str2,滿足條件,所以k=2;

根據上面的條件,可以輕易寫出next的計算函數;

//計算next數組,t為計算字符串
void calculateNext(int  & next,string t)
{
    for(int j=0;j<t.size();j++)
    {
        bool state=true;//是否是等于0的情況
        if(j==0)
            next=-1;
        else 
        {
            int k=j-1;//0<k<j,獲取最大的k
            while(k>0)
            {
                string str1="",str2="";
                for(int i=0;i<=k-1;i++)
                {
                    str1=str1+t[i];
                    str2=str2+t[j-k+i];
                }
                if(str1==str2)//若,p[0....k-1]=p[j-k...j-1];
                {
*(next+j)=k; state=false;//不是等于0的情況 break; } k--;

        }
        if(state)<strong>//滿足其他條件,即為0</strong> *(next+j)=0;
    }

}

}</pre>

計算出匹配信息next后,我們就可以進行KMP匹配了,

主要思路是:

假設i和j分別為指示主串和模式串中正在比較的字符的當前位置,并對i 和j 賦初值0。
在匹配的過程中,若si=tj,則i和j分別增加1,繼續進行比較。
否則,若j=0,si!=tj,則從s的下一個字符開始,即i增加一,若j!=0,令j=next[j]

理解了上面的思路,就很容易編寫出KMP匹配代碼:

//kmp匹配算法,s為主串,t為模式串
int KMP(int * next,string s,string t)
{
    int j=0,i=0;//i為主串s正在匹配的字符,j為t正在匹配的字符

while(i<s.size()&&j<t.size())
{
    if(t[j] == s[i])
    {
        j++;i++;
    }
    else
    {
        if(j==0) 
        {
            i++;//若第一個字符就匹配失敗,則從s的下一個字符開始
        }
        else
        {

            j = *(next+j);//,也可以j=next[j],用next確定t應回溯到的字符
        }
    }
}
if(j < t.size())//整個過程匹配失敗
{
    return -1;
}
return i-t.size();//匹配成功
//第二種寫法
/*2. while(i < s.size())  
     {  
              while(j > -1 && t[j] != s[i])  
                      j = next[j];  
              i++, j++;  
              if(j >= t.size()) return i - j;  
     }  
 return -1;  */



}</pre>

至此,我們就完成了KMP算法的學習。在學習KMP過程中,我發了一篇“另一種”有關的KMP算法,更好理解學習,附上鏈接:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

本次KMP的源代碼地址:https://github.com/longpo/algorithm


來自:http://hm4123660.iteye.com/blog/2194569

 本文由用戶 lidki 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!