KMP字串匹配學習筆記

Ivanovcraft 發表於 2021-04-08

部分內容引自皎月半灑花的部落格



模式串匹配問題模型

給定一個需要處理的文字串和一個需要在文字串中搜尋的模式串,查詢在該文字串中,給出的模式串的出現有無、次數、位置等。

演算法思想

每次失配之後不會從頭開始列舉,而會從最大可能匹配位置開始重新匹配

考慮資料

模式串:abcabc
文字串:abcabdababcabc


匹配過程中文字串的$d$和模式串末位的$c$出現了失配,由目前的匹配結果可知模式串的前五位$abcab$與文字串失配位置前的五位匹配,取模式串中匹配的部分$S$,當且僅當$S$中有任何與字尾相同的字首,滿足該字首一定與文字串失配位置前的若干位匹配,取滿足上述條件的最大字首與文字串匹配,再嘗試重新匹配下一位(這一位置即為最大可能匹配位置),重複該過程直到匹配成功或不存在這樣的字首時結束

模式串:   abcabc
文字串:abcabdababcabc


$next$陣列

$next[i]$表示前$i-1$位(從第0位開始)匹配而第$i$位失配時應跳轉到的位置,該位置滿足前$next[i]-1$位仍與文字串匹配

$next$陣列的處理

自匹配,使模式串與自身進行匹配,匹配過程中處理出下一位的$next$值,即當前位的最大匹配的後一位

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e6+10;
char s[maxn],st[maxn];
int n,m,nxt[maxn];
void getnext()    //自匹配(next陣列的處理)
{
    for(int j=0,i=1;i<m;i++)
    {
        while(j&&s[i]!=s[j])
            j=nxt[j];
        nxt[i+1]=(j+=s[i]==s[j]);
        //若i,j位置匹配成功,顯然s[1~j]是s[1~i]的最大的,滿足前字尾相等的字首,則j+1為s[i]的最大可能匹配位置
        //否則j必為0,意為沒有任何成功的匹配
    }
}
int main()
{
    scanf("%s%s",st,s);
    n=strlen(st),m=strlen(s);
    getnext();
    for(int j=0,i=0;i<n;i++)
    //i為文字串位置,j為模式串當前嘗試匹配的位置
    {
        while(j&&st[i]!=s[j])
        //重複取最大可能匹配位置,直到匹配成功或不存在任何可能的匹配
            j=nxt[j];
        if(st[i]==s[j])//若匹配成功,模式串嘗試匹配的位置後移一位
            if(++j==m)
            {
                printf("%d\n",i-j+2);
                j=nxt[j];
                //若匹配成功則統計答案並重置匹配位置
            }
    }
    for(int i=1;i<=m;i++)
        printf("%d ",nxt[i]);
    printf("\n");
    return 0;
}