拓展KMP分析

2021-09-12 10:16:51 字數 3599 閱讀 1641

拓展kmp是對kmp演算法的擴充套件,它解決如下問題:

定義母串s,和字串t,設s的長度為n,t的長度為m,求t與s的每乙個字尾的最長公共字首,也就是說,設extend陣列,extend[i]表示t與s[i,n-1]的最長公共字首,要求出所有extend[i](0<=i

注意到,如果有乙個位置extend[i]=m,則表示t在s**現,而且是在位置i出現,這就是標準的kmp問題,所以說拓展kmp是對kmp演算法的擴充套件,所以一般將它稱為擴充套件kmp演算法。

下面舉乙個例子,s=」aaaabaa」,t=」aaaaa」,首先,計算extend[0]時,需要進行5次匹配,直到發生失配。

從而得知extend[0]=4,下面計算extend[1],在計算extend[1]時,是否還需要像計算extend[0]時從頭開始匹配呢?答案是否定的,因為通過計算extend[0]=4,從而可以得出s[0,3]=t[0,3],進一步可以得到 s[1,3]=t[1,3],計算extend[1]時,事實上是從s[1]開始匹配,設輔助陣列next[i]表示t[i,m-1]和t的最長公共字首長度。在這個例子中,next[1]=4,即t[0,3]=t[1,4],進一步得到t[1,3]=t[0,2],所以s[1,3]=t[0,2],所以在計算extend[1]時,通過extend[0]的計算,已經知道s[1,3]=t[0,2],所以前面3個字元已經不需要匹配,直接匹配s[4]和t[3]即可,這時一次就發生失配,所以extend[1]=3。這個例子很有代表性,有興趣的讀者可以繼續計算完剩下的extend陣列。

1. 拓展kmp演算法一般步驟

通過上面的例子,事實上已經體現了拓展kmp演算法的思想,下面來描述拓展kmp演算法的一般步驟。

首先我們從左到右依次計算extend陣列,在某一時刻,設extend[0...k]已經計算完畢,並且之前匹配過程中所達到的最遠位置為p,所謂最遠位置,嚴格來說就是i+extend[i]-1的最大值(0<=i<=k),並且設取這個最大值的位置為po,如在上乙個例子中,計算extend[1]時,p=3,po=0。

現在要計算extend[k+1],根據extend陣列的定義,可以推斷出s[po,p]=t[0,p-po],從而得到 s[k+1,p]=t[k-po+1,p-po],令len=next[k-po+1],(回憶下next陣列的定義),分兩種情況討論:

第一種情況:k+len

如下圖所示:

上圖中,s[k+1,k+len]=t[0,len-1],然後s[k+len+1]一定不等於t[len],因為如果它們相等,則有s[k+1,k+len+1]=t[k+po+1,k+po+len+1]=t[0,len],那麼next[k+po+1]=len+1,這和next陣列的定義不符(next[i]表示t[i,m-1]和t的最長公共字首長度),所以在這種情況下,不用進行任何匹配,就知道extend[k+1]=len。

第二種情況: k+len>=p

如下圖:

上圖中,s[p+1]之後的字元都是未知的,也就是還未進行過匹配的字串,所以在這種情況下,就要從s[p+1]和t[p-k+1]開始一一匹配,直到發生失配為止,當匹配完成後,如果得到的extend[k+1]+(k+1)大於p則要更新未知p和po。

至此,拓展kmp演算法的過程已經描述完成,細心地讀者可能會發現,next陣列是如何計算還沒有進行說明,事實上,計算next陣列的過程和計算extend[i]的過程完全一樣,將它看成是以t為母串,t為字串的特殊的拓展kmp演算法匹配就可以了,計算過程中的next陣列全是已經計算過的,所以按照上述介紹的演算法計算next陣列即可,這裡不再贅述。

2. 時間複雜度分析

下面來分析一下演算法的時間複雜度,通過上面的演算法介紹可以知道,對於第一種情況,無需做任何匹配即可計算出extend[i],對於第二種情況,都是從未被匹配的位置開始匹配,匹配過的位置不再匹配,也就是說對於母串的每乙個位置,都只匹配了一次,所以演算法總體時間複雜度是o(n)的,同時為了計算輔助陣列next[i]需要先對字串t進行一次拓展kmp演算法處理,所以拓展kmp演算法的總體複雜度為o(n+m)的。其中n為母串的長度,m為子串的長度。

下面是拓展kmp演算法的一道例題。

給你乙個字串ss

,然後再給你nn

個詢問,第ii

個詢問給你乙個數字kk

和乙個字串mm

這個詢問的答案是tt

的最小長度,其中tt

是ss的子串,且tt

中必須出現kk

個mm 第一行乙個字串ss

第二行數字qq

,代表詢問次數

後面每一行乙個kk

和mm,代表依次詢問

資料保證所有字母均為小寫字母(1

≤|s|

≤105)

(1≤|s|≤105)(1

≤q≤10

5)(1≤q≤105)(1

≤ki≤

|s|)

(1≤ki≤|s|)

所有詢問長度加起來小於

100000

100000

保證所有字串不相同

qq行,每行代表乙個詢問的答案

無解請輸出−1

−1 sample input

sample output

aaaaa

53 a

3 aa

2 aaa

3 aaaa

1 aaaaa

344

-15

abbb

74 b

1 ab

3 bb

1 abb

2 bbb

1 a2 abbb

-12-1

3-11-1

**為:

#include

using namespace std;

const int maxn=100010;     

int net[maxn],ex[maxn];   

char s1[maxn],s2[maxn];

void getnext(char *str)  

{  int i=0,j,po,len=strlen(str);  

net[0]=len; 

while(str[i]==str[i+1]&&i+1>s1;

len=strlen(s1);

cin>>n;

while(n--)

{cin>>k>>s2;

int len2=strlen(s2),sum=0,temp,cnt=-1;

exkmp(s1,s2);

for(int i=0;i

posted @

2018-06-07 00:49

songhl 閱讀(

...)

編輯收藏

拓展KMP分析

拓展kmp是對kmp演算法的擴充套件,它解決如下問題 定義母串s,和字串t,設s的長度為n,t的長度為m,求t與s的每乙個字尾的最長公共字首,也就是說,設extend陣列,extend i 表示t與s i,n 1 的最長公共字首,要求出所有extend i 0 i 注意到,如果有乙個位置extend...

拓展KMP演算法

問題模型 給定字串s和子串t,s的長度為n,t的長度為m 求字串t與字串s的每乙個字尾d的最長公共字首。拓展kmp演算法 假設 extend 陣列 extend i 表示 t 與 s i,n 的最長公共字首,目的是求出所有的 extend 0 n 1 注意到,如果存在extend i m,則說明 t...

拓展kmp演算法總結

演算法總結第二彈,上次總結了下kmp,這次就來拓展kmp吧。拓展kmp是對kmp演算法的擴充套件,它解決如下問題 定義母串s,和字串t,設s的長度為n,t的長度為m,求t與s的每乙個字尾的最長公共字首,也就是說,設extend陣列,extend i 表示t與s i,n 1 的最長公共字首,要求出所有...