KMP算法:
關鍵是利用匹配失敗後的信息,儘量減少模式串與主串的匹配次數以達到快速匹配的目的。具體實現就是實現一個next()函數,函數本身包含了模式串的局部匹配信息。時間複雜度O(m+n)。
個人對於Next()函數的理解:
一:思路概括:我語文不太好可以忽略,可以先看手工實現
1,把將要進行next計算的字符串S分成 k ,j 前後兩串,k代表前串開頭所在的序號,j代表後串開頭所在的序號,起始的時候j=1,k=0。
2,我們比較一下前串 後串是否相等,要怎麼比較呢,肯定是比較S[j]==S[k],如果相等,那麼next[j+1]=k+1,然後j++,k++。
關鍵就是理解這個next[j+1]=k+1(爲什麼k+1,由於下標是從0開始?):簡單說就是S串中的第j+1個字符的next函數值由他前面的字符與前串相等的個數來決定,就是說串中的第j+1個字符的next函數值,是由他前面的字符串決定的
3,當S[j]!=S[k],即不相等的時侯,那麼j不動,k返回到開頭(因該是next[k]位置,便於理解先假設是返回k=0處),即從頭比較S[0]與S[j],S[1]與S[j+1]
例如:第 j+1 個字符的next函數值next[j+1]等於3,意味着 他的前三個字符串,S[j-2]S[j-1]S[j] =S[0]S[1]S[2]
二:手工實現理解
例1:
序號 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
子串 |
a |
b |
c |
a |
a |
b |
c |
b |
a |
Next值 |
-1 |
0 |
0 |
0 |
1 |
1 |
2 |
3 |
0 |
1,第一個字符的next值令爲-1。令第二個字符b的next值爲0,初始k=0,j=1, 比較S[k] 和S[j]
2,比較S[0] !=S[1] 所以 j++ k不變 next[j=2]=0
3,比較S[0] !=S[2] 所以 j++ k不變 next[3]=0
4,比較S[0] ==S[3] 所以 j++,k++, next[4]=k=1
5,k=1了 所以比較S[1] !=S[4],k返回到next[k]位置,即k=next[1]=0,然後比較S[k=0] == S[4] 所以 j++ ,k++ ,next[5]=k=1
6,比較S[1] ==S[5] 所以 j++ ,k++ ,next[6]=k=2
7,比較S[2] ==S[6] 所以 j++ ,k++ ,next[7]=k=3
8,比較S[3] !=S[7] 所以k返回到next[k=3]位置,即k=next[3]=0,然後比較S[k=0] != S[7] 所以 j++ ,不變k=0不變,next[8]=k=0
完畢
可以輕鬆的發現,S[j]的比較,決定了字符 S[j+1 ] 的next函數值
例二:在例一中,每次不相等時返回的都是k=next[k]=0,都是返回到了開頭,我們看一個不是返回到開頭0的情況:
序號 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
子串 |
a |
a |
b |
c |
a |
a |
a |
b |
a |
a |
c |
Next值 |
-1 |
0 |
1 |
0 |
0 |
1 |
2 |
2 |
3 |
1 |
2 |
從 j=5,k=1的時候開始
5,比較 S[1] == S[5] 所以 j++,k++,next[j+1=6]=k=2
6,比較S[2] != S[6] 所以 k返回到next[k=2]位置,即k=next[2]=1,然後比較S[k=1] == S[6] 所以 j++ ,k=1+1=2,next[7]=k=2
…………
因此,我們發現K的退回 是退回到next[k]的位置 即S[j]!=S[k]時,k=next[k]
二:getNext函數實現代碼如下
void getNext(char *p,int *next)
{
int j,k;
next[0]=-1;
j=0; //後串起始位置,一直增加
k=-1; //k==-1時,代表j++進入下一輪匹配,k代表前串起始位置,匹配失敗回到-1
while(j<strlen(p)-1)
{
if(k==-1||p[j]==p[k]) //匹配的情況下,p[j]==p[k],next[j+1]=k+1;
{
++j;
++k;
next[j]=k;
}
else //p[j]!=p[k],k=next[k]
k=next[k];
}
}
KMP算法那完整實現代碼如下
#include<stdio.h>
#include<string.h>
int next[30];
void getNext(char *p,int *next)
{
int j,k;
next[0]=-1;
j=0; //後串起始位置,一直增加
k=-1; //k==-1時,代表j++進入下一輪匹配,k代表前串起始位置,匹配失敗回到-1
while(j<strlen(p)-1)
{
if(k==-1||p[j]==p[k]) //匹配的情況下,p[j]==p[k],next[j+1]=k+1;
{
++j;
++k;
next[j]=k;
}
else //p[j]!=p[k],k=next[k]
k=next[k];
}
}
int my_kmp(char* s1,char* key){
int i=0;
int j=0;
int l1=strlen(s1);
int l2=strlen(key);
while((i<l1)&&(j<l2)){
if(j==-1||s1[i]==key[j]){
i++;
j++;
}
else{
//i=i-j+1;j=0;
j=next[j];
}
}
if(j>=l2)return i-l2;
else return 0;
}
int main(){
char* s="aabcaaabaac";
getNext(s,next);
printf("%d",1+my_kmp("aabaabbccaabbaaccaaabccbcaacccb",s));
}