轉(zhuǎn)載比較詳細(xì)
KMP字符串模式匹配詳解
KMP字符串模式匹配通俗點(diǎn)說就是一種在一個(gè)字符串中定位另一個(gè)串的高效算法。簡(jiǎn)單匹配算法的時(shí)間復(fù)雜度為O(m*n);KMP匹配算法??梢宰C明它的時(shí)間復(fù)雜度為O(m+n).。
一.簡(jiǎn)單匹配算法
先來看一個(gè)簡(jiǎn)單匹配算法的函數(shù):
int Index_BF ( char S [ ], char T [ ], int pos )
{
/* 若串 S 中從第pos(S 的下標(biāo)0≤pos<StrLength(S))個(gè)字符
起存在和串 T 相同的子串,則稱匹配成功,返回第一個(gè)
這樣的子串在串 S 中的下標(biāo),否則返回 -1 */
int i = pos, j = 0;
while ( S[i+j] != '\0'&& T[j] != '\0')
if ( S[i+j] == T[j] )
j ++; // 繼續(xù)比較后一字符
else
{
i ++; j = 0; // 重新開始新的一輪匹配
}
if ( T[j] == '\0')
return i; // 匹配成功 返回下標(biāo)
else
return -1; // 串S中(第pos個(gè)字符起)不存在和串T相同的子串
} // Index_BF
此算法的思想是直截了當(dāng)?shù)模簩⒅鞔?/span>S中某個(gè)位置i起始的子串和模式串T相比較。即從 j=0 起比較 S[i+j] 與 T[j],若相等,則在主串 S 中存在以 i 為起始位置匹配成功的可能性,繼續(xù)往后比較( j逐步增1 ),直至與T串中最后一個(gè)字符相等為止,否則改從S串的下一個(gè)字符起重新開始進(jìn)行下一輪的"匹配",即將串T向后滑動(dòng)一位,即 i 增1,而 j 退回至0,重新開始新一輪的匹配。
例如:在串S=”abcabcabdabba”中查找T=” abcabd”(我們可以假設(shè)從下標(biāo)0開始):先是比較S[0]和T[0]是否相等,然后比較S[1] 和T[1]是否相等…我們發(fā)現(xiàn)一直比較到S[5] 和T[5]才不等。如圖:
當(dāng)這樣一個(gè)失配發(fā)生時(shí),T下標(biāo)必須回溯到開始,S下標(biāo)回溯的長(zhǎng)度與T相同,然后S下標(biāo)增1,然后再次比較。如圖:
這次立刻發(fā)生了失配,T下標(biāo)又回溯到開始,S下標(biāo)增1,然后再次比較。如圖:
這次立刻發(fā)生了失配,T下標(biāo)又回溯到開始,S下標(biāo)增1,然后再次比較。如圖:
又一次發(fā)生了失配,所以T下標(biāo)又回溯到開始,S下標(biāo)增1,然后再次比較。這次T中的所有字符都和S中相應(yīng)的字符匹配了。函數(shù)返回T在S中的起始下標(biāo)3。如圖:

二. KMP匹配算法
還是相同的例子,在S=”abcabcabdabba”中查找T=”abcabd”,如果使用KMP匹配算法,當(dāng)?shù)谝淮嗡阉鞯?/span>S[5] 和T[5]不等后,S下標(biāo)不是回溯到1,T下標(biāo)也不是回溯到開始,而是根據(jù)T中T[5]==’d’的模式函數(shù)值(next[5]=2,為什么?后面講),直接比較S[5] 和T[2]是否相等,因?yàn)橄嗟龋?/span>S和T的下標(biāo)同時(shí)增加;因?yàn)橛窒嗟龋?/span>S和T的下標(biāo)又同時(shí)增加。。。最終在S中找到了T。如圖:
KMP匹配算法和簡(jiǎn)單匹配算法效率比較,一個(gè)極端的例子是:
在S=“AAAAAA…AAB“(100個(gè)A)中查找T=”AAAAAAAAAB”, 簡(jiǎn)單匹配算法每次都是比較到T的結(jié)尾,發(fā)現(xiàn)字符不同,然后T的下標(biāo)回溯到開始,S的下標(biāo)也要回溯相同長(zhǎng)度后增1,繼續(xù)比較。如果使用KMP匹配算法,就不必回溯.
對(duì)于一般文稿中串的匹配,簡(jiǎn)單匹配算法的時(shí)間復(fù)雜度可降為O (m+n),因此在多數(shù)的實(shí)際應(yīng)用場(chǎng)合下被應(yīng)用。
KMP算法的核心思想是利用已經(jīng)得到的部分匹配信息來進(jìn)行后面的匹配過程??辞懊娴睦?。為什么T[5]==’d’的模式函數(shù)值等于2(next[5]=2),其實(shí)這個(gè)2表示T[5]==’d’的前面有2個(gè)字符和開始的兩個(gè)字符相同,且T[5]==’d’不等于開始的兩個(gè)字符之后的第三個(gè)字符(T[2]=’c’).如圖:
也就是說,如果開始的兩個(gè)字符之后的第三個(gè)字符也為’d’,那么,盡管T[5]==’d’的前面有2個(gè)字符和開始的兩個(gè)字符相同,T[5]==’d’的模式函數(shù)值也不為2,而是為0。
前面我說:在S=”abcabcabdabba”中查找T=”abcabd”,如果使用KMP匹配算法,當(dāng)?shù)谝淮嗡阉鞯?/span>S[5] 和T[5]不等后,S下標(biāo)不是回溯到1,T下標(biāo)也不是回溯到開始,而是根據(jù)T中T[5]==’d’的模式函數(shù)值,直接比較S[5] 和T[2]是否相等。。。為什么可以這樣?
剛才我又說:“(next[5]=2),其實(shí)這個(gè)2表示T[5]==’d’的前面有2個(gè)字符和開始的兩個(gè)字符相同”。請(qǐng)看圖 :因?yàn)椋?/span>S[4] ==T[4],S[3] ==T[3],根據(jù)next[5]=2,有T[3]==T[0],T[4] ==T[1],所以S[3]==T[0],S[4] ==T[1](兩對(duì)相當(dāng)于間接比較過了),因此,接下來比較S[5] 和T[2]是否相等。。。
有人可能會(huì)問:S[3]和T[0],S[4] 和T[1]是根據(jù)next[5]=2間接比較相等,那S[1]和T[0],S[2] 和T[0]之間又是怎么跳過,可以不比較呢?因?yàn)?/span>S[0]=T[0],S[1]=T[1],S[2]=T[2],而T[0] != T[1], T[1] != T[2],==> S[0] != S[1],S[1] != S[2],所以S[1] != T[0],S[2] != T[0]. 還是從理論上間接比較了。
有人疑問又來了,你分析的是不是特殊輕況啊。
假設(shè)S不變,在S中搜索T=“abaabd”呢?答:這種情況,當(dāng)比較到S[2]和T[2]時(shí),發(fā)現(xiàn)不等,就去看next[2]的值,next[2]=-1,意思是S[2]已經(jīng)和T[0] 間接比較過了,不相等,接下來去比較S[3]和T[0]吧。
假設(shè)S不變,在S中搜索T=“abbabd”呢?答:這種情況當(dāng)比較到S[2]和T[2]時(shí),發(fā)現(xiàn)不等,就去看next[2]的值,next[2]=0,意思是S[2]已經(jīng)和T[2]比較過了,不相等,接下來去比較S[2]和T[0]吧。
假設(shè)S=”abaabcabdabba”在S中搜索T=“abaabd”呢?答:這種情況當(dāng)比較到S[5]和T[5]時(shí),發(fā)現(xiàn)不等,就去看next[5]的值,next[5]=2,意思是前面的比較過了,其中,S[5]的前面有兩個(gè)字符和T的開始兩個(gè)相等,接下來去比較S[5]和T[2]吧。
總之,有了串的next值,一切搞定。那么,怎么求串的模式函數(shù)值next[n]呢?(本文中next值、模式函數(shù)值、模式值是一個(gè)意思。)
三. 怎么求串的模式值next[n]
定義:
(1)next[0]= -1 意義:任何串的第一個(gè)字符的模式值規(guī)定為-1。
(2)next[j]= -1 意義:模式串T中下標(biāo)為j的字符,如果與首字符
相同,且j的前面的1—k個(gè)字符與開頭的1—k
個(gè)字符不等(或者相等但T[k]==T[j])(1≤k<j)。
如:T=”abCabCad” 則 next[6]=-1,因T[3]=T[6]
(3)next[j]=k 意義:模式串T中下標(biāo)為j的字符,如果j的前面k個(gè)
字符與開頭的k個(gè)字符相等,且T[j] != T[k] (1≤k<j)。
即T[0]T[1]T[2]。。。T[k-1]==
T[j-k]T[j-k+1]T[j-k+2]…T[j-1]
且T[j] != T[k].(1≤k<j);
(4) next[j]=0 意義:除(1)(2)(3)的其他情況。
舉例:
01)求T=“abcac”的模式函數(shù)的值。
next[0]= -1 根據(jù)(1)
next[1]=0 根據(jù) (4) 因(3)有1<=k<j;不能說,j=1,T[j-1]==T[0]
next[2]=0 根據(jù) (4) 因(3)有1<=k<j;(T[0]=a)!=(T[1]=b)
next[3]= -1 根據(jù) (2)
next[4]=1 根據(jù) (3) T[0]=T[3] 且 T[1]=T[4]
即
下標(biāo)
|
0
|
1
|
2
|
3
|
4
|
T
|
a
|
b
|
c
|
a
|
c
|
next
|
-1
|
0
|
0
|
-1
|
1
|
若T=“abcab”將是這樣:
下標(biāo)
|
0
|
1
|
2
|
3
|
4
|
T
|
a
|
b
|
c
|
a
|
b
|
next
|
-1
|
0
|
0
|
-1
|
0
|
為什么T[0]==T[3],還會(huì)有next[4]=0呢, 因?yàn)?/span>T[1]==T[4], 根據(jù) (3)” 且T[j] != T[k]”被劃入(4)。
02)來個(gè)復(fù)雜點(diǎn)的,求T=”ababcaabc” 的模式函數(shù)的值。
next[0]= -1 根據(jù)(1)
next[1]=0 根據(jù)(4)
next[2]=-1 根據(jù) (2)
next[3]=0 根據(jù) (3) 雖T[0]=T[2] 但T[1]=T[3] 被劃入(4)
next[4]=2 根據(jù) (3) T[0]T[1]=T[2]T[3] 且T[2] !=T[4]
next[5]=-1 根據(jù) (2)
next[6]=1 根據(jù) (3) T[0]=T[5] 且T[1]!=T[6]
next[7]=0 根據(jù) (3) 雖T[0]=T[6] 但T[1]=T[7] 被劃入(4)
next[8]=2 根據(jù) (3) T[0]T[1]=T[6]T[7] 且T[2] !=T[8]
即
下標(biāo)
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
T
|
a
|
b
|
a
|
b
|
c
|
a
|
a
|
b
|
c
|
next
|
-1
|
0
|
-1
|
0
|
2
|
-1
|
1
|
0
|
2
|
只要理解了next[3]=0,而不是=1,next[6]=1,而不是= -1,next[8]=2,而不是= 0,其他的好象都容易理解。
03) 來個(gè)特殊的,求 T=”abCabCad” 的模式函數(shù)的值。
下標(biāo)
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
T
|
a
|
b
|
C
|
a
|
b
|
C
|
a
|
d
|
next
|
-1
|
0
|
0
|
-1
|
0
|
0
|
-1
|
4
|
next[5]= 0 根據(jù) (3) 雖T[0]T[1]=T[3]T[4],但T[2]==T[5]
next[6]= -1 根據(jù) (2) 雖前面有abC=abC,但T[3]==T[6]
next[7]=4 根據(jù) (3) 前面有abCa=abCa,且 T[4]!=T[7]
若T[4]==T[7],即T=” adCadCad”,那么將是這樣:next[7]=0, 而不是= 4,因?yàn)?/span>T[4]==T[7].
下標(biāo)
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
T
|
a
|
d
|
C
|
a
|
d
|
C
|
a
|
d
|
next
|
-1
|
0
|
0
|
-1
|
0
|
0
|
-1
|
0
|
如果你覺得有點(diǎn)懂了,那么
練習(xí):求T=”AAAAAAAAAAB” 的模式函數(shù)值,并用后面的求模式函數(shù)值函數(shù)驗(yàn)證。
意義:
next 函數(shù)值究竟是什么含義,前面說過一些,這里總結(jié)。
設(shè)在字符串S中查找模式串T,若S[m]!=T[n],那么,取T[n]的模式函數(shù)值next[n],
1. next[n]= -1 表示S[m]和T[0]間接比較過了,不相等,下一次比較 S[m+1] 和T[0]
2. next[n]=0 表示比較過程中產(chǎn)生了不相等,下一次比較 S[m] 和T[0]。
3. next[n]= k >0 但k<n, 表示,S[m]的前k個(gè)字符與T中的開始k個(gè)字符已經(jīng)間接比較相等了,下一次比較S[m]和T[k]相等嗎?
4. 其他值,不可能。
四. 求串T的模式值next[n]的函數(shù)
說了這么多,是不是覺得求串T的模式值next[n]很復(fù)雜呢?要叫我寫個(gè)函數(shù)出來,目前來說,我寧愿去登天。好在有現(xiàn)成的函數(shù),當(dāng)初發(fā)明KMP算法,寫出這個(gè)函數(shù)的先輩,令我佩服得六體投地。我等后生小子,理解起來,都要反復(fù)琢磨。下面是這個(gè)函數(shù):
void get_nextval(const char *T, int next[])
{
// 求模式串T的next函數(shù)值并存入數(shù)組 next。
int j = 0, k = -1;
next[0] = -1;
while ( T[j/*+1*/] != '\0' )
{
if (k == -1 || T[j] == T[k])
{
++j; ++k;
if (T[j]!=T[k])
next[j] = k;
else
next[j] = next[k];
}// if
else
k = next[k];
}// while
////這里是我加的顯示部分
// for(int i=0;i<j;i++)
//{
// cout<<next[i];
//}
//cout<<endl;
}// get_nextval
另一種寫法,也差不多。
void getNext(const char* pattern,int next[])
{
next[0]= -1;
int k=-1,j=0;
while(pattern[j] != '\0')
{
if(k!= -1 && pattern[k]!= pattern[j] )
k=next[k];
++j;++k;
if(pattern[k]== pattern[j])
next[j]=next[k];
else
next[j]=k;
}
////這里是我加的顯示部分
// for(int i=0;i<j;i++)
//{
// cout<<next[i];
//}
//cout<<endl;
}
下面是KMP模式匹配程序,各位可以用他驗(yàn)證。記得加入上面的函數(shù)
#include <iostream.h>
#include <string.h>
int KMP(const char *Text,const char* Pattern) //const 表示函數(shù)內(nèi)部不會(huì)改變這個(gè)參數(shù)的值。
{
if( !Text||!Pattern|| Pattern[0]=='\0' || Text[0]=='\0' )//
return -1;//空指針或空串,返回-1。
int len=0;
const char * c=Pattern;
while(*c++!='\0')//移動(dòng)指針比移動(dòng)下標(biāo)快。
{
++len;//字符串長(zhǎng)度。
}
int *next=new int[len+1];
get_nextval(Pattern,next);//求Pattern的next函數(shù)值
int index=0,i=0,j=0;
while(Text[i]!='\0' && Pattern[j]!='\0' )
{
if(Text[i]== Pattern[j])
{
++i;// 繼續(xù)比較后繼字符
++j;
}
else
{
index += j-next[j];
if(next[j]!=-1)
j=next[j];// 模式串向右移動(dòng)
else
{
j=0;
++i;
}
}
}//while
delete []next;
if(Pattern[j]=='\0')
return index;// 匹配成功
else
return -1;
}
int main()//abCabCad
{
char* text="bababCabCadcaabcaababcbaaaabaaacababcaabc";
char*pattern="adCadCad";
//getNext(pattern,n);
//get_nextval(pattern,n);
cout<<KMP(text,pattern)<<endl;
return 0;
}
五.其他表示模式值的方法
上面那種串的模式值表示方法是最優(yōu)秀的表示方法,從串的模式值我們可以得到很多信息,以下稱為第一種表示方法。第二種表示方法,雖然也定義next[0]= -1,但后面絕不會(huì)出現(xiàn)-1,除了next[0],其他模式值next[j]=k(0≤k<j)的意義可以簡(jiǎn)單看成是:下標(biāo)為j的字符的前面最多k個(gè)字符與開始的k個(gè)字符相同,這里并不要求T[j] != T[k]。其實(shí)next[0]也可以定義為0(后面給出的求串的模式值的函數(shù)和串的模式匹配的函數(shù),是next[0]=0的),這樣,next[j]=k(0≤k<j)的意義都可以簡(jiǎn)單看成是:下標(biāo)為j的字符的前面最多k個(gè)字符與開始的k個(gè)字符相同。第三種表示方法是第一種表示方法的變形,即按第一種方法得到的模式值,每個(gè)值分別加1,就得到第三種表示方法。第三種表示方法,我是從論壇上看到的,沒看到詳細(xì)解釋,我估計(jì)是為那些這樣的編程語(yǔ)言準(zhǔn)備的:數(shù)組的下標(biāo)從1開始而不是0。
下面給出幾種方法的例子:
表一。
下標(biāo)
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
T
|
a
|
b
|
a
|
b
|
c
|
a
|
a
|
b
|
c
|
(1) next
|
-1
|
0
|
-1
|
0
|
2
|
-1
|
1
|
0
|
2
|
(2) next
|
-1
|
0
|
0
|
1
|
2
|
0
|
1
|
1
|
2
|
(3) next
|
0
|
1
|
0
|
1
|
3
|
0
|
2
|
1
|
3
|
第三種表示方法,在我看來,意義不是那么明了,不再討論。
表二。
下標(biāo)
|
0
|
1
|
2
|
3
|
4
|
T
|
a
|
b
|
c
|
A
|
c
|
(1)next
|
-1
|
0
|
0
|
-1
|
1
|
(2)next
|
-1
|
0
|
0
|
0
|
1
|
表三。
下標(biāo)
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
T
|
a
|
d
|
C
|
a
|
d
|
C
|
a
|
d
|
(1)next
|
-1
|
0
|
0
|
-1
|
0
|
0
|
-1
|
0
|
(2)next
|
-1
|
0
|
0
|
0
|
1
|
2
|
3
|
4
|
對(duì)比串的模式值第一種表示方法和第二種表示方法,看表一:
第一種表示方法next[2]= -1,表示T[2]=T[0],且T[2-1] !=T[0]
第二種表示方法next[2]= 0,表示T[2-1] !=T[0],但并不管T[0] 和T[2]相不相等。
第一種表示方法next[3]= 0,表示雖然T[2]=T[0],但T[1] ==T[3]
第二種表示方法next[3]= 1,表示T[2] =T[0],他并不管T[1] 和T[3]相不相等。
第一種表示方法next[5]= -1,表示T[5]=T[0],且T[4] !=T[0],T[3]T[4] !=T[0]T[1],T[2]T[3]T[4] !=T[0]T[1]T[2]
第二種表示方法next[5]= 0,表示T[4] !=T[0],T[3]T[4] !=T[0]T[1] ,T[2]T[3]T[4] !=T[0]T[1]T[2],但并不管T[0] 和T[5]相不相等。換句話說:就算T[5]==’x’,或 T[5]==’y’,T[5]==’9’,也有next[5]= 0 。
從這里我們可以看到:串的模式值第一種表示方法能表示更多的信息,第二種表示方法更單純,不容易搞錯(cuò)。當(dāng)然,用第一種表示方法寫出的模式匹配函數(shù)效率更高。比如說,在串S=“adCadCBdadCadCad 9876543”中匹配串T=“adCadCad”, 用第一種表示方法寫出的模式匹配函數(shù),當(dāng)比較到S[6] != T[6] 時(shí),取next[6]= -1(表三),它可以表示這樣許多信息: S[3]S[4]S[5]==T[3]T[4]T[5]==T[0]T[1]T[2],而S[6] != T[6],T[6]==T[3]==T[0],所以S[6] != T[0],接下來比較S[7]和T[0]吧。如果用第二種表示方法寫出的模式匹配函數(shù),當(dāng)比較到S[6] != T[6] 時(shí),取next[6]= 3(表三),它只能表示:S[3]S[4]S[5]== T[3]T[4]T[5]==T[0]T[1]T[2],但不能確定T[6]與T[3]相不相等,所以,接下來比較S[6]和T[3];又不相等,取next[3]= 0,它表示S[3]S[4]S[5]== T[0]T[1]T[2],但不會(huì)確定T[3]與T[0]相不相等,即S[6]和T[0] 相不相等,所以接下來比較S[6]和T[0],確定它們不相等,然后才會(huì)比較S[7]和T[0]。是不是比用第一種表示方法寫出的模式匹配函數(shù)多繞了幾個(gè)彎。
為什么,在講明第一種表示方法后,還要講沒有第一種表示方法好的第二種表示方法?原因是:最開始,我看嚴(yán)蔚敏的一個(gè)講座,她給出的模式值表示方法是我這里的第二種表示方法,如圖:
她說:“next 函數(shù)值的含義是:當(dāng)出現(xiàn)S[i] !=T[j]時(shí),下一次的比較應(yīng)該在S[i]和T[next[j]] 之間進(jìn)行。”雖簡(jiǎn)潔,但不明了,反復(fù)幾遍也沒明白為什么。而她給出的算法求出的模式值是我這里說的第一種表示方法next值,就是前面的get_nextval()函數(shù)。匹配算法也是有瑕疵的。于是我在這里發(fā)帖說她錯(cuò)了:
現(xiàn)在看來,她沒有錯(cuò),不過有張冠李戴之嫌。我不知道,是否有人第一次學(xué)到這里,不參考其他資料和明白人講解的情況下,就能搞懂這個(gè)算法(我的意思是不僅是算法的大致思想,而是為什么定義和例子中next[j]=k(0≤k<j),而算法中next[j]=k(-1≤k<j))。
憑良心說:光看這個(gè)講座,我就對(duì)這個(gè)教受十分敬佩,不僅講課講得好,聲音悅耳,而且這門課講得層次分明,恰到好處。在KMP這個(gè)問題上出了點(diǎn)小差錯(cuò),可能
是編書的時(shí)候,在這本書上抄下了例子,在那本書上抄下了算法,結(jié)果不怎么對(duì)得上號(hào)。因?yàn)槲覜]找到原書,而據(jù)有的網(wǎng)友說,書上已不是這樣,也許吧。說起來,
教授們研究的問題比這個(gè)高深不知多少倍,哪有時(shí)間推演這個(gè)小算法呢。總之,瑕不掩玉。
書歸正傳,下面給出我寫的求第二種表示方法表示的模式值的函數(shù),為了從S的任何位置開始匹配T,“當(dāng)出現(xiàn)S[i] !=T[j]時(shí),下一次的比較應(yīng)該在S[i]和T[next[j]] 之間進(jìn)行。” 定義next[0]=0 。
void myget_nextval(const char *T, int next[])
{
// 求模式串T的next函數(shù)值(第二種表示方法)并存入數(shù)組 next。
int j = 1, k = 0;
next[0] = 0;
while ( T[j] != '\0' )
{
if(T[j] == T[k])
{
next[j] = k;
++j; ++k;
}
else if(T[j] != T[0])
{
next[j] = k;
++j;
k=0;
}
else
{
next[j] = k;
++j;
k=1;
}
}//while
for(int i=0;i<j;i++)
{
cout<<next[i];
}
cout<<endl;
}// myget_nextval
下面是模式值使用第二種表示方法的匹配函數(shù)(next[0]=0)
int my_KMP(char *S, char *T, int pos)
{
int i = pos, j = 0;//pos(S 的下標(biāo)0≤pos<StrLength(S))
while ( S[i] != '\0' && T[j] != '\0' )
{
if (S[i] == T[j] )
{
++i;
++j; // 繼續(xù)比較后繼字符
}
else // a b a b c a a b c
// 0 0 0 1 2 0 1 1 2
{ //-1 0 -1 0 2 -1 1 0 2
i++;
j = next[j]; /*當(dāng)出現(xiàn)S[i] !=T[j]時(shí),
下一次的比較應(yīng)該在S[i]和T[next[j]] 之間進(jìn)行。要求next[0]=0。
在這兩個(gè)簡(jiǎn)單示范函數(shù)間使用全局?jǐn)?shù)組next[]傳值。*/
}
}//while
if ( T[j] == '\0' )
return (i-j); // 匹配成功
else
return -1;
} // my_KMP
六.后話--KMP的歷史
[這段話是抄的]
Cook于1970年證明的一個(gè)理論得到,任何一個(gè)可以使用被稱為下推自動(dòng)機(jī)的計(jì)算機(jī)抽象模型來解決的問題,也可以使用一個(gè)實(shí)際的計(jì)算機(jī)(更精確的說,使用一個(gè)隨機(jī)存取機(jī))在與問題規(guī)模對(duì)應(yīng)的時(shí)間內(nèi)解決。特別地,這個(gè)理論暗示存在著一個(gè)算法可以在大約m+n的時(shí)間內(nèi)解決模式匹配問題,這里m和n分別是存儲(chǔ)文本和模式串?dāng)?shù)組的最大索引。Knuth 和Pratt努力地重建了 Cook的證明,由此創(chuàng)建了這個(gè)模式匹配算法。大概是同一時(shí)間,Morris在考慮設(shè)計(jì)一個(gè)文本編輯器的實(shí)際問題的過程中創(chuàng)建了差不多是同樣的算法。這里可以看到并不是所有的算法都是“靈光一現(xiàn)”中被發(fā)現(xiàn)的,而理論化的計(jì)算機(jī)科學(xué)確實(shí)在一些時(shí)候會(huì)應(yīng)用到實(shí)際的應(yīng)用中。