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;
}
}
}