KMP算法(學習筆記)

KMP算法總結(南昌理工ACM集訓)

(這幾天想題目想的腦殼疼)

  • 什麼是KMP算法

(我準備引用別人的話,講滴非常好)
Knuth-Morris-Pratt字符串查找算法(簡稱爲KMP算法,0.0)可在一個主文本字符串S內查找一個詞W的出現位置。
此算法通過運用對這個詞在不匹配時本身就包含足夠的信息來確定下一個匹配將在哪裏開始的發現,從而避免重新檢查先前匹配的字符。

  • 爲什麼我們要用KMP算法呢

我們先介紹一下普通匹配字符串的算法
題目
給定一個模式串S,以及一個模板串P,所有字符串中只包含大小寫英文字母以及阿拉伯數字。
模板串P在模式串S中多次作爲子串出現。
求出模板串P在模式串S中所有出現的位置的起始下標。



  • 樸素算法(暴力)

(借一下y總的模板題來用一下,哈哈)

#include<iostream>

using namespace std;

int main(){
   
   
    string a,b;
    cin>>a>>b;
    for(int i=0;i<a.size()-b.size()+1;i++){
   
   
        string c=a.substr(i,b.size());
        if(c==b) cout<<i<<" ";
    }
}

但是當字母長度很大時,就會超時。
於是KMP算法就誕生了(哈哈,問題造就算法)

  • -KMP算法解決

等等等一下,我先介紹怎麼用KMP算法

  • 原理
    母串 abcdabc
    字串 abc
    我們如果用樸素算法的話,就是一個一個從母串中找。
    但是我們讓一個字符串匹配了很多次,很浪費效率,大大提高了時間。
    比如




母串 abcdabc
字串 abc
母串 abcdabc
字串 *abc
母串 abcdabc
字串 **abc
母串 abcdabc
字串 ***abc
母串 abcdabc
字串 ****abc








這樣匹配很重複,字符需要匹配多次 (難過呀)

  • kmp算法實現
    kmp算法由兩個東西組成 next數組和 kmp匹配。

  • next數組是什麼呢
    next數組主要儲存的是子串的前後綴的位置
    在p字符串中
    p [1- j ] = p [ i - j + 1 , i];


  • kmp匹配
    這是子串和母串進行匹配,通過next數組避免一個字符多次重複的匹配

  • 模板

//next數組
for (int i = 2, j = 0; i <= m; i ++ )
{
   
   
    while (j && p[i] != p[j + 1]) j = ne[j];
    if (p[i] == p[j + 1]) j ++ ;
    ne[i] = j;
}
// kmp匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
   
   
    while (j && s[i] != p[j + 1]) j = ne[j];
    if (s[i] == p[j + 1]) j ++ ;
    if (j == m)
    {
   
   
    	// 匹配成功後的邏輯
        j = ne[j];
    }
}

大家是不是覺得很簡單,如果明白可以做一下這道題

【模板】KMP字符串匹配

這樣你已經有了一點入門的知識了~~(因爲KMP算法遠沒有這麼簡單,哈哈)~~

現在我們必須加大難度對於你們對KMP算法的瞭解了

熟練掌握next數組,這道題了解一下

做完了,看來你現在已經有一定的框架了

來一道CF的KMP試試吧
這道題比較全面的展現了kmp的強大

這個題比較具體的闡述了next數組和kmp匹配

看看我的AC代碼 (AC時的喜悅,哈哈)

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int q[]={
   
   1,2,3};
char p[4][N];
int ne[4][N];
int k[4][4];
int len[4];
int ans=0x7fffffff;
void getnext(int m,char p[],int k){
   
   
    for(int i=2,j=0;i<=m;i++){
   
   
        while(j&&p[i]!=p[j+1]) j=ne[k][j];
        if(p[i]==p[j+1]) j++;
        ne[k][i]=j;
    }
}
int kmp(int n,int m,char s[],char p[],int k){
   
   
    int j=0;
    for(int i=1;i<=n;i++){
   
   
        while(j&&s[i]!=p[j+1]) j=ne[k][j];
        if(s[i]==p[j+1]) j++;
        if(j==m) return -1;
    }
    return j;
}
void solve(int i,int j,int l){
   
   
    if(k[i][j]>=0&&k[j][l]>=0) ans=min(ans,len[i]+len[j]+len[l]-k[i][j]-k[j][l]);
    else{
   
   
        if(k[i][j]<0&&k[j][l]<0) ans=min(ans,len[l]);
        else if(k[i][j]<0) ans=min(ans,len[j]+len[l]-k[j][l]);
        else if(k[j][l]<0) ans=min(ans,len[i]+len[l]-k[i][l]);
    }
}
int main(){
   
   
    scanf("%s%s%s",p[1]+1,p[2]+1,p[3]+1);
    for(int i=1;i<=3;i++) len[i]=strlen(p[i]+1);
    for(int i=1;i<=3;i++){
   
   
        getnext(len[i],p[i],i);
        for(int j=1;j<=3;j++){
   
   
            if(i==j) continue;
            k[i][j]=kmp(len[j],len[i],p[j],p[i],i);
        }
    }
    do{
   
   
        solve(q[0],q[1],q[2]);
    }while(next_permutation(q,q+3));
    cout<<ans;
    return 0;
}

學習快樂,算法漫漫路,我很開心~~(Orz,來救救我)~~
萌新的一點點總結,歡迎大佬來指點0.0
點個贊吧,我打的很辛苦滴 看看孩子吧/(ㄒoㄒ)/~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章