給定字符串s和p,尋找字符串p在字符串s中出現的位置,暴力解法以下所示:算法
++i;++j
,繼續匹配下一字符。s[i]
與s[j]
匹配失敗,令i-=(j-1),j=0
,即i轉到上次首次匹配開頭字符的下一位置,j從頭開始。int brute_match(string s, string p) { int slen = s.size(); int plen = p.size(); int i = 0, j = 0; while (i < slen and j < plen) { if (s[i] == p[j]) { ++i; ++j; } else { i -= (j - 1); j = 0; } } if (j == plen) return i - j; return -1; }
若是有一種算法可以不讓i回退,只須要移動j,那麼咱們的算法將會大爲簡化。數組
在kmp算法中,引入了next數組,表示當前字符以前的子字符串中具備多大長度的相同的前綴後綴,請注意,next字符串的內容是和須要匹配的字符串p相關的。code
在brute-force算法的基礎上改進的kmp算法以下所示:blog
int kmp_match(string s, string p,const vector<int> &next) { int slen = s.size(); int plen = p.size(); int i = 0, j = 0; while (i < slen and j < plen) { if (j == -1 or s[i] == p[j]) { ++i; ++j; } else { j = next[j]; } } if (j == plen) return i - j; return -1; }
str
len
中每個位置和字符串s
一對應,表示字符串截止到當前位置最長相同前綴後綴的長度。k
表示當前位置及以前的字符串的最長相同前綴後綴的長度。s[k]
表示原字符串s
在最長前綴後綴str
以後緊跟的那個字符。s[i]!=s[k]
,表明字符i
沒法進一步與字符j
匹配,最長相同前綴後綴不可能在上一次匹配的基礎之上進一步增加。若是以前可以匹配的最長相同前綴後綴長度大於0,咱們不斷嘗試在上一次的基礎之上下降標準,匹配更小長度的最長前綴後綴。k
調整爲k=len[k-1]
,緣由稍後詳述。s[i]=s[j]
,這表示最長相同前綴與後綴可以在長度爲k
的最長相同前綴後綴的基礎之上,再增加一個字符,即len[i]=++k
。不然說明當前位置沒有相同前綴後綴,記len[i]=0
。只有長度大於1的字符串纔有最長前綴與後綴,最長前綴不包括最後一個字符,最長後綴不包括第一個字符。字符串
k=len[k-1]
緣由講解假設當前k
指向字符串中f
對應的位置,在e
以前咱們匹配到的最長相同前綴後綴爲abeab
,咱們發現e
與f
不能匹配,咱們須要下降標準,嘗試匹配更短長度的最長前綴後綴,應該匹配多長的呢?string
咱們發現圖中下劃線部分的字符串str
徹底相同,咱們要找的長度縮減的最長相同前綴後綴長度不能超過str
的長度,並且要保證這一字符串(設爲str_new
)知足條件:是相同的前綴後綴。即圖中最前面和最後面標記的字符串str_new
必須徹底同樣。根據對稱性,這等價於在靠後的str
中尋找最長前綴後綴。並且由於k比較的時候,k
實際指向前面str
的末尾下一位置,因此咱們有:k=len[k-1]
。基礎
void calculate_length(string s, vector<int> &len) { len.resize(s.length()); len[0] = 0; int k = 0; for (int i = 1; i < s.length(); ++i) { while (s[i] != s[k] and k > 0) { k = len[k - 1]; } if (s[i] == s[k]) { len[i] = ++k; } else { len[i] = 0; } } }
next數組,表示當前字符以前的子字符串中具備多大長度的相同的前綴後綴,也就是說next 數組至關於「最大長度值」 總體向右移動一位,而後初始值賦爲-1。im
其示例代碼以下所示,由於和求最長前綴後綴的代碼相似,故再也不追究。next
void calculate_next(string s,vector<int>&next){ next.resize(s.length()); int len = s.length(); next[0] = -1; int k = -1; int j = 0; while(j<len-1){ if(k == -1 or s[j] ==s[k]){ ++k; ++j; next[j] = k; } else{ k = next[k]; } } }