KMP 大問題分割成小問題 分步理解
阿新 • • 發佈:2020-12-19
KMP
分解成小問題:
- 求出next陣列
- 求字串字首與字尾的公共子串長度
- 當pattern與text不匹配時,按照next陣列的指示進行跳轉
求字串字首與字尾的公共子串長度
字串 absdc:
字首:a, ab, abs, absd, absdc
真字首:a, ab, abs, absd
字尾:c, dc, sdc, bsdc, absdc
真字尾:c, dc, sdc, bsdc
用於next陣列的應該是真字首與真字尾的公共子串最大長度。
int tmp = 0, res = 0; int len = s.length(); int j = 0; for(int i = 1; i < len; i++){ if(s[i] == s[j]){ tmp ++;j++; res = tmp > res ? tmp : res; } else if(s[i] != s[j]){ j = 0; tmp = 0; if(s[i] == s[j]){ tmp ++;j++; res = tmp > res ? tmp : res; } } } cout << res;
這個是針對確定的一個字串的比較暴力的演算法,一旦不匹配就從頭重新匹配。(因為針對確定的一個字串,所以字首都是從第一個開始的嘛,不匹配就從頭再判斷咯)
求next陣列
其實構建next陣列的本質就是求解pattern
中的 字首子串中的 最長公共前後綴。
next[i] = k pattern[i]之前的子串中,存在最長長度為k的公共前後綴,因此可以把當前不匹配的text的位,與k+1位pattern再次進行比較。(但是因為陣列是從0開始的,所以下標可以直接被賦值next陣列中的值,而不用+1
也就是它需要進行len-1
次的上一節的行為。同時需要考慮儘可能重複利用結果(有點像KMP中pattern
text
不匹配時就使用next陣列跳躍,這裡也是利用next陣列中已經產生的資料進行跳躍。
想象兩個指標,一個指向字首的最後,一個指向字尾的最後,當它們的值不同時:
i -> 字首最後 j->從0到len-1迴圈的下標
pattern[i] != pattern[j]時
讓i往回走,類似kmp
i = next[i];//現在i是最長公共前後綴中字首的後一位的下標
當它們的值相同時,那公共前後綴的長度增加:
next[++j] = ++i;注意next陣列的下標對應的迴圈變數j
使用next時,記住使用的下標的值是不匹配的當位。
//字串陣列下標從0開始 int next[pattern.length()+1]; next[0] = -1;//用於pattern與text進行匹配且第一個數不匹配時 int i = 0, j = -1;//i在前面 j在後面 next[++i] = ++j;//初始化 int len = pattern.length(); while(i < len){ if(pattern[i] == pattern[j])i next[++i] = ++j; else i = next[i]; }
把pattern和text進行匹配,利用next陣列加速
j = 0; i = 0;//j是text的下標,i是pattern的下標
while(j < text.length()){
while(i >= 0 && pattern[i] != text[j]){
i = next[i];
}
//迴圈退出,說明i<0或者當前位匹配
i++; j++;//繼續向後匹配
//進行跳出判斷
if(i == len)
return j-len
}