1. 程式人生 > 其它 >[模板] KMP演算法

[模板] KMP演算法

想要完全理解還是有些困難的,這裡簡單敘述一下思路:
在求next陣列時,把“字尾”的p串固定在上方,依次遍歷每個元素作為截斷的末尾,通過移動下方的p串使其字首與上方p串的字尾重合,求出其next。
假設已經求出了next[i-1] = j, 考慮next[i]
如果p[j+1] == p[i], 什麼也不用做,可得next[i] = j+1
如果p[j+1] != p[i], 需要將下方的p串向後移動,令j = next[j]可以很快地移動到恰當位置,遞迴下去直到找到j滿足p[j+1] = p[i],即可得到next[i] = j+1。
如果直到j = 1都不能滿足p[j+1] = p[i],則next[i] = 0
稍加整合可以得到

for (int i = 2, j = 0; i <= n; i++) {
  while (j && p[j + 1] != p[i]) j = ne[j];
  if (p[j + 1] == p[i]) j++;
  ne[i] = j;
}

關於為何next[1]=0:
根據kmp的過程我們可以知道,當p的長度大於1是這樣做是正確的。
而當p的長度等於1時,每次迴圈中j一開始始終是0,每次其實就是比較了s[i]的某個元素和p[1],從而判斷了next[i]為1還是0
至此我們已經求出模式串每一個位置的最大公共前後綴,kmp匹配的過程和上述過程非常相似,只需要注意匹配成功後令j = next[j]可以順利讓匹配繼續進行

for (int i = 1, j = 0; i <= m; i++) {
  while (j && p[j + 1] != s[i]) j = ne[j];
  if (p[j + 1] == s[i]) j++;
  if (j == n) {
    printf("%d ", i - n + 1);
    j = ne[j];
  }
}