1. 程式人生 > 實用技巧 >資料結構/ 串的模式匹配法 / kmp演算法與next陣列的構造

資料結構/ 串的模式匹配法 / kmp演算法與next陣列的構造

模式匹配的基本思想:存在主串S和模式串T,從S的第pos個字元起和T的第一個字元相比較,若相等,逐個比較後續字元;若不相等,從S的pos+1個字元旗重新依次匹配, 直到T中的每個字元和S中的一個連續 字元序列相等,即為匹配成功。

可以據此情景,有這麼一道題:

題目描述

如題,給出兩個字串s1s2,其中s2s1的子串,求出s2s1中所有出現的位置。

輸入格式:

第一行為一個字串,即為s1(僅包含大寫字母)

第二行為一個字串,即為s2(僅包含大寫字母)

輸出格式:

若干行,每行包含一個整數,表示s2s1中出現的位置

輸入輸出樣例

輸入樣例#1

2222bokeyuan111

bokeyuan

輸出樣例#1

5

輸入樣例#2

acabaabaabcacaabc

aabcac

輸出樣例#2

6

通常的做法是:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string str1,str2;
    cin>>str1;              //主串
    cin>>str2;              //模式串
    int len1=str1.size();   //取長
    int len2=str2.size();

    int j=0;
    
for(int i=0; i<len1; i++) { if(str1[i]==str2[j]) //相等,往後匹配 { j++; if(j==len2) //全部匹配,輸出出現的初始位置 { cout<<i-j+2<<endl; j=0; } } else //不相等,換新的起點 { j
=0; } } }

執行正常:

這個演算法的時間複雜度是O(M*N),效率較低,只是簡單地不斷重複迴圈和判斷。

然後引入一種優化演算法,這個演算法是D.EKnuth、V.R.Pratt、J.H.Morris同時發現的,因此稱為克努特——莫里斯——普拉特演算法,KMP演算法。此演算法可以在O(M+N)的時間數量級上完成串的模式匹配操作。這種演算法不太容易理解,最關鍵的是構造next陣列

eg.我們引入模式串[T]={abaabcac},next有如下結果:

j 1 2 3 4 5 6 7 8
模式串T a b a a b c a c
next 0 1 1 2 2 3 1 2

計算某字元的next時,看該字元的前一個字元T[j-1],和以這個前字元的next為下標的字元T[netx[j-1]]是否相等,若相等,則該字元的next=這兩字元的next之和。

如:在計算next[6]時,T[j-1]=T[6-1]=T[5]=b, T[next[j-1]]=T[next[5]]=T[2]=b, b==b, 所以next[6]=next[5]+next[2]=1+2=3

j 1 2 3 4 5 6 7 8
模式串T a b a a b c a c
next 0 1 1 2 2 3 1 2

若不相等,則按這個方法繼續向前尋找,直到找到相同的

若找到起始字元也才相同,則該字元的next額外+1

若找到起始字元也不相同,則該字元的next=1

如:在計算next[7]時,c!=a

j 1 2 3 4 5 6 7 8
模式串T a b a a b c a c
next 0 1 1 2 2 3 1 2

程式碼:

#include"iostream"
#include"string.h"

using namespace std;


void fun(char s[],int next[],int len)
{
    next[1]=0;
    next[2]=1;
    for(int i=3;i<=len;i++)
    {
        int j=i-1;
        while(s[i-1]!=s[next[j]] && j>1)
            j=next[j];
        if(j>1 && s[i-1]==s[next[j]])
            next[i]=next[j]+1;
        else
            next[i]=1;
    }


}

void kmp(char p1[],char p2[],int next[],int len)
{
    for(int i=1,j=0;i<=len;i++)
    {
        while(p1[i]!=p2[j+1] && j>0)
            j=next[j];
        if(p1[i]==p2[j+1])
            ++j;
        if(j==next[0])
        {
            cout<<i-next[0]+1<<endl;
            j=next[0];
            break;
        }
        if(i==len)
        {
            cout<<"-1"<<endl;
        }
    }
}
int main()
{
    char p[100];
    char s[100];
    int next[100];
    cin>>p+1;
    int lenp=strlen(p+1);

    cin>>s+1;
    int len=strlen(s+1);

    next[0]=len;

    fun(s,next,len);
    kmp(p,s,next,lenp);

    for(int q=1;q<=len;q++)  cout<<next[q]<<"  ";
    return 0;
}
void getnext(string T,int next[])
{
    i=1;
    next[1]=0;
    j=0;
    while(i<T[0])
    {
        if(j==0||T[J]==T[I])
        {
            ++i;
            ++j;
            next[i]=j;
        }
        else
            j=next[j];
    }
}

採納:https://blog.csdn.net/you_will_know_me/article/details/77102567https://blog.csdn.net/suguoliang/article/details/77460455