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 ...