KMP初步

KMP算法

介紹

用處:用於串的模式匹配,即找出模式串在主串中的出現位置c++

樸素想法,直接遍歷兩個串,失配回到主串開始比較位置的下一位繼續匹配,複雜度\(O(nm)\)算法

KMP算法即在\(O(n+m)\)複雜度內匹配的算法數組

算法實現

經過一個叫\(next\)數組的東西,使指向主串的\(i\)指針不回溯,只改變指向模式串的\(j\)指針spa

因此重點就是在於失配後\(j\)移動到哪裏,也就是關於\(next\)數組的部分指針

\(Next[]\)的基本意義:code

在串ss中尋找串s,blog

當前已經獲得了\(s[1…j-1]=ss[i-j+1…i-1]\)字符串

正在檢查\(s[j]與ss[i]\)是否相等,發現不相同,失配,get

(若是相同,i和j自增1,繼續向後檢查)it

若是按照暴力算法,ss上的「指針」須要回到i-j+1,s上的「指針」j須要回到0;

咱們試圖讓i往回不移動,僅僅是讓j往回移動,移動到一個合適的、不會丟失任何匹配可能的位置,這個位置就是咱們的Next[j]。

\(next\)數組的定義是

即最大的\(P[1 , k-1] = P[j-k+1 ,j-1]\)

求出next以後在失配時讓\(j=next[j]\)就是KMP算法了

如何求出\(next\)

已知 Next[j]=kn。

即存在 \(s[1,kn-1]=s[j-kn+1,j-1]\)

(最長後綴等於字符串前綴)

分狀況討論,對於\(j+1\)(如今須要求的Next數組位)有兩種狀況

  1. 若$ s[kn]=s[j]$,那麼能夠獲得 \(s[1,kn]=s[j-kn+1,j]\) ,根據定義,不難推斷出\(Next[j+1]=kn+1\)

  2. 若不相等,則轉化爲匹配自身的問題,不斷地令\(j=Next[j]\)直至出現 \(s[kn]=s[j]\)(轉化爲1狀況,\(Next[j+1]=kn+1\));或者\(j=1\)(找不到後綴等於前綴)匹配到頭,\(Next[j+1]=1\)

模版

#include <bits/stdc++.h>
#define N 1000005
using namespace std;

int n, m;

int nxt[N];
int s[N], t[N];

void getNext(int t[]) {
    int i = 1, j = 0;
    nxt[1] = 0;
    while (i < m) {
        if (j == 0 || t[i] == t[j]) {
            i++; j++;
            nxt[i] = j;
        }
        else j = nxt[j];
    }
}

int kmp(int s[], int t[], int pos) {
    int i = pos, j = 1;
    while (i <= n && j <= m) {
        if (j == 0 || s[i] == t[j]) {
            i++; j++;
        }
        else j = nxt[j];
    }
    if (j > m) return i - m;
    else return -1;
}

int main() {
    int t1;
    scanf("%d", &t1);
    while (t1--) {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &s[i]);
        }
        for (int i = 1; i <= m; i++) {
            scanf("%d", &t[i]);
        }
        memset(nxt, 0, sizeof(nxt));
        getNext(t);
        printf("%d\n", kmp(s, t, 1));
    }
    return 0;
}
相關文章
相關標籤/搜索