KMP演算法詳解及各種應用

2021-06-16 08:48:13 字數 4286 閱讀 5687

kmp演算法詳解:

kmp演算法之所以叫做kmp演算法是因為這個演算法是由三個人共同提出來的,就取三個人名字的首字母作為該演算法的名字。其實kmp演算法與bf演算法的區別就在於kmp演算法巧妙的消除了指標i的回溯問題,只需確定下次匹配j的位置即可,使得問題的複雜度由o(mn)下降到o(m+n)。

在kmp演算法中,為了確定在匹配不成功時,下次匹配時j的位置,引入了next陣列,next[j]的值表示p[0...j-1]中最長字尾的長度等於相同字串行的字首。

對於next陣列的定義如下:

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

2) next[j]=max k:00時,表示p[0...k-1]=p[j-k,j-1]

因此kmp演算法的思想就是:在匹配過程稱,若發生不匹配的情況,如果next[j]>=0,則目標串的指標i不變,將模式串的指標j移動到next[j]的位置繼續進行匹配;若next[j]=-1,則將i右移1位,並將j置0,繼續進行比較。

**實現如下:

int kmpmatch(char *s,char *p)

else

if(j == strlen(p))

return i - strlen(p);

}return -1;

}

因此kmp演算法的關鍵在於求算next陣列的值,即求算模式串每個位置處的最長字尾與字首相同的長度, 而求算next陣列的值有兩種思路,第一種思路是用遞推的思想去求算,還有一種就是直接去求解。

1、按照遞推的思想:

根據定義next[0]=-1,假設next[j]=k, 即p[0...k-1]==p[j-k,j-1]

1)若p[j]==p[k],則有p[0..k]==p[j-k,j],很顯然,next[j+1]=next[j]+1=k+1;

2)若p[j]!=p[k],則可以把其看做模式匹配的問題,即匹配失敗的時候,k值如何移動,顯然k=next[k]。

因此可以這樣去實現:

void getnext(char *p,int *next)

else //p[j]!=p[k]

k = next[k];

}}

2、直接求解方法

void getnext(char *p,int *next)

else if(i == 1)

else

}if(j == 0)

next[i] = 0;

}}}

bool equals(char *p,int i,int j) //判斷p[0...j-1]與p[i-j...i-1]是否相等

return true;

}

給定乙個字串,問最多是多少個相同子串不重疊連線構成。

kmp的next陣列應用。這裡主要是如何判斷是否有這樣的子串,和子串的個數。

若為abababa,顯然除其本身外,沒有子串滿足條件。而分析其next陣列,next[7] = 5,next[5] = 3,next[3] = 1,即str[2..7]可由ba子串連線構成,那怎麼否定這樣的情況呢?很簡單,若該子串滿足條件,則len%sublen必為0。sunlen可由上面的分析得到為len-next[len]。

因為子串是首尾相接,len/sublen即為substr的個數。

若l%(l-next[l])==0,n = l/(l-next[l]),else n = 1

#include#includeusing namespace std;

char pattern[1000002];

int next[1000002];

/*kmp演算法,需要首先求出模式串的next函式值

next[j] = k,說明 p0pk-1 == pj-kpj-1,也就是說k為其前面相等串的長度

*/void get_nextval(const char* pattern)

else

j=next[j]; //若j值不相同,則j值回溯

}}//get_nextval

int main(void)

return 0;

}

大意:定義字串a,若a最多由n個相同字串s連線而成,則a=s^n,如"aaa" = "a"^3,"abab" = "ab"^2

"ababa" = "ababa"^1

給出乙個字串a,求該字串的所有字首中有多少個字首sa= s^n(n>1)

輸出符合條件的字首長度及其對應的n

如aaa

字首aa的長度為2,由2個'a'組成

字首aaa的長度為3,由3個"a"組成

分析:kmp

若某一長度l的字首符合上訴條件,則

1.next[l]!=0(next[l]=0時字串為原串,不符合條件)

2.l%(l-next[l])==0(此時字串的長度為l/next[l])

對於2:有str[0]....str[next[l]-1]=str[l-next[l]-1]...str[l-1]

=》str[l-next[l]-1] = str[l-next[l]-1+l-next[l]-1] = str[2*(l-next[l]-1)];

假設s = l-next[l]-1;則有str[0]=str[s]=str[2*s]=str[3*s]...str[k*s],對於所有i%s==0,均有s[i]=s[0]

同理,str[1]=str[s+1]=str[2*s+1]....

str[j]=str[s+j]=str[2*s+j]....

綜上,若l%s==0,則可得l為str[0]...str[s-1]的相同字串組成,

總長度為l,其中字串長度sl = s-0+1=l-next[l],迴圈次數為l/sl

故對於所有大於1的字首,只要其符合上述條件,即為答案之一

#include#include#includeusing namespace std;

char pattern[1000002];

int next[1000002];

/*kmp演算法,需要首先求出模式串的next函式值

next[j] = k,說明 p0pk-1 == pj-kpj-1,也就是說k為其前面相等串的長度

*/void get_nextval(const char* pattern)

else

j=next[j];

}}//get_nextval

int main(void)

printf("\n");

}return 0;

}

大意:給出乙個字串a,求a有多少個字首同時也是字尾,從小到大輸出這些字首的長度。

分析:kmp

對於長度為len的字串,由next的定義知:

a[0]a[1]...a[next[len]-1]=a[len-next[len]]...a[len-1]此時a[0]a[1]...a[next[len]-1]為乙個符合條件的字首

有a[0]a[1]....a[next[next[len]]-1] = a[len-next[next[len] - next[next[len]]]...a[next[len]-1],故a[0]a[1]....a[next[next[len]]-1]也是乙個符合條件的字首

故從len=>next[len]=>next[next[len]] ....=>直到某個next為0均為合法答案,注意當首位單詞相同時,也為答案。

#include#include#include#includeusing namespace std;

char pattern[400002];

int next[400002];

/*kmp演算法,需要首先求出模式串的next函式值

next[j] = k,說明 p0pk-1 == pj-kpj-1,也就是說k為其前面相等串的長度

*/void get_nextval(const char* pattern)

else

j=next[j];

}}//get_nextval

int main(void)

if(pattern[0]==pattern[n-1]) //首部、尾部字元相同

ans.push_back(1);

i=ans.size()-1;

for(;i>0;i--)

printf("%d ",ans[i]);

printf("%d\n",ans[0]);

} return 0;

}

KMP演算法模板及各種應用

給定乙個字串,問最多是多少個相同子串不重疊連線構成。kmp的next陣列應用。這裡主要是如何判斷是否有這樣的子串,和子串的個數。若為abababa,顯然除其本身外,沒有子串滿足條件。而分析其next陣列,next 7 5,next 5 3,next 3 1,即str 2.7 可由ba子串連線構成,那...

KMP演算法及應用

kmp演算法用來解決一系列字串單模式匹配問題,其以難理解,難記憶著稱。其next陣列的構造就如同ac自動機中的fail指標,就是如果匹配失敗,字串應從 開始繼續匹配。這裡的next陣列表示 next i 前i個字元的公共最長前字尾長度。覺得對於kmp演算法,這篇寫的不錯 現在來講一下應用。給定兩個字...

KMP演算法詳解及模板

kmp 演算法是用來解決單模匹配問題的一種演算法。如果暴力的進行單模匹配,那麼時間複雜度為o nm kmp 演算法通過對模式串的預處理優化了複雜度。為了敘述方便,設模式串長度為n,主串長度為m。將模式串稱為s1,主串稱為s2,下標從1 開始。我們首先對模式串預處理出乙個next 陣列。next i ...