1. 程式人生 > >KMP演算法之next函式解釋(大量的反證法 和數學歸納法來襲)

KMP演算法之next函式解釋(大量的反證法 和數學歸納法來襲)

先放get_nextval()函式的程式碼

void get_nextval(const char str[],int *net)
{
    net[0]=-1;
    int j=0,k=-1,len;
    len=strlen(str);
    while(j<len)
    {
        if(k==-1||str[j]==str[k])
            net[++j]=++k;
        else
            k=net[k];
    }
}//在這個函式內陣列下標是0開始的 

在這個函式內陣列下標是0開始的 

首先你要明白next陣列的含義

next[j]的意思就是在下標j之前的字串上  得出該字串大的最大匹配數

就像這樣

 要求k最大即匹配數最多(兩條黑線內的對應相等)(圖不好先湊合一下 有時間找大牛給補下圖)

 

好的 現在咱們開始分析get_next函式

next[0]=-1(代表沒有最大匹配 應該將i指標右移動)

假設next[j]=k成立  且0<=k<j (k!=j保證最大匹配數不為自己本身)

1.如果str[j]==str[k]的話str[j+1]=k+1

          因為如果還存在str[j+1]>k+1的話即 str[j]>k  這與假設不成立    所以 str[j+1]=k+1

2.如果str[j]!=str[k]的話 

          那就一直找k=next[k]直至 str[j]=str[k]  然後令str[j+1]=k+1

也許很多人對第二個條件不太明白,那麼下面咱們來證明第二個條件的正確性,

剛開始我一直有一個疑問 為什麼next[j+1]的值一定會通過這個方法找出來呢  萬一存在一個更長的字串與之匹配呢

好的  現在來看一下圖  , 假設我們通過第二個條件找到答案是k3即str[j]==str[k3] ,  但存在一個k'沒有通過第二個條件找到(且k'>k3 要求k'更長)使得 str[0]......str[k']==str[j-k']......str[j](前k'+1個字元相等) 即next[j+1]=k'+1   

此時k'>k3  且k'沒有被第二個條件所找到

因為k3<k'<j    那麼 k'的長度一定在找到的k之間

假設在k'的長度在k1和k2之間 且k2<k'<k1,那麼k1判斷之後發現str[j]!=str[k1],下一步k2=next[k1], 但是因為k2<k'<k1,即k2並不是k1的最大匹配, k'的匹配比k2更大 next[k1]的值應該是k';

與假設next[k1]==k2矛盾 

故不存在一個沒有找到過的k'   使得k'>k3且 str[0]......str[k']==str[j-k']......str[j]str[j]==str[k']

所以next[j+1]的值肯定能從第二個條件找到。

 

get_nextval()函式還有一點可能有人有疑問,為什麼當k=-1時 也執行next[++j]=++k

因為當k==-1時 必定有過一次k=0且str[j]!=str[0]   即沒有   以0為下標的字串 與  j之前的字串 匹配 返回-1 此時因該讓next[j+1]=0

 

數學歸納法

初始條件:next[0]=-1 符合題意  

假設當n<=j    時候next[j]符合題意 

又因為當n=j+1 時 由next[j+1]也符合題意

所以演算法正確

 

/*
字串kmp
*/
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1010;
void get_nextval(const char str[],int *net)
{
    net[0]=-1;
    int j=0,k=-1,len;
    len=strlen(str);
    while(j<len)
    {
        if(k==-1||str[j]==str[k])
            net[++j]=++k;
        else
            k=net[k];
    }
}
int kmp(const char ss[],const char tt[])//引數傳兩個主川和模式串  返回值-1或者在主串中匹配的位置
{
    int *net=new int[maxn];
    int ls,lt,i=0,j=0;
    ls=strlen(ss);
    lt=strlen(tt);
    get_nextval(tt,net);
    while(i<ls&&j<lt&&(ls-i)>=(lt-j))
    {
        if(ss[i]==tt[j])
        {
            i++;
            j++;
        }
        else//不匹配
        {
            int k=net[j];
            if(k==-1)
            {
                i++;
                j=0;
            }
            else
                j=k;
        }
    }
    delete []net;
    if(j>=lt)
        return (i-j+1);
    return -1;
}
int main()
{
    char ss[maxn],tt[maxn];
    while(~scanf("%s %s",ss,tt))
    {
        int ans=kmp(ss,tt);
        if(ans==-1)
            cout<<"未匹配"<<endl;
        else
        {
            cout<<"從主串的第 "<<ans<<" 個存在匹配"<<endl;
        }
    }
}