KMP算法(学习笔记)

KMP算法总结(南昌理工ACM集训)

(这几天想题目想的脑壳疼)

  • 什么是KMP算法

(我准备引用别人的话,讲滴非常好)
Knuth-Morris-Pratt字符串查找算法(简称为KMP算法,0.0)可在一个主文本字符串S内查找一个词W的出现位置。
此算法通过运用对这个词在不匹配时本身就包含足够的信息来确定下一个匹配将在哪里开始的发现,从而避免重新检查先前匹配的字符。

  • 为什么我们要用KMP算法呢

我们先介绍一下普通匹配字符串的算法
题目
给定一个模式串S,以及一个模板串P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模板串P在模式串S中多次作为子串出现。
求出模板串P在模式串S中所有出现的位置的起始下标。

  • 朴素算法(暴力)

(借一下y总的模板题来用一下,哈哈)

#include

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
#include
#include
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ㄒ)/~~

你可能感兴趣的:(算法)