我对KMP算法的简单理解

我对KMP算法的简单理解

前言:字符串匹配问题

问题概述:

“字符串A是否为字符串B的子串?如果是,出现在B的什么位置?”这个问题就是字符串匹配问题。字符串A称为模式串(zs),字符串B称为主串(ss)。

其中,解决字符串匹配问题有两种方法:

  • 暴力求解(朴素模式匹配算法)
  • KMP算法

朴素模式匹配算法

思路

从主串的第一个字符开始与模式串的第一个字符进行比较,如果相同则模式串指针和主串的指针都往下走一位,再去进行比较。如果相同,则主串指针向下移动一位,模式串的指针回到初始的地方,重新开始进行比较。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
// 暴力求得子串
int re_index(string ss,string sz){
    
    
    int len_s = ss.length();
    int len_z = sz.length();
    int i = 0;
    int j = 0;
    while(i<len_s&&j<len_z){
    
    
        if(ss[i]==sz[j]){
    
    
            i++; j++;
        }else{
    
    
            i = i - j + 1; //i要从开始的地方+1再进行向后遍历
            j = 0;
        }
    }
    if(j==len_z){
    
    
        return i-j; // 回到i刚刚开始的地方
    }else{
    
    
        return -1;
    }
}
int main()
{
    
    
    string ss,sz;
    cin>>ss>>sz;
    cout<<re_index(ss,sz)<<endl;
    return 0;                   
}

KMP算法

KMP算法可以先读一下这两篇文章:

什么是KMP算法(详解)

可能是全网最清晰的KMP算法讲解

之后,KMP算法就可以分为两个部分:

K M P 算法 = 求 n e x t 数组 + 遍历求下标 KMP算法 = 求next数组 + 遍历求下标 KMP算法=next数组+遍历求下标

求next数组 == 求最长的公共前后缀

  • 公共前后缀

    一个字符串的前n个字符和后n个字符,分别是它的前缀和后缀,如果它的前缀和后缀相同,则它们的长度就是公共前后缀的长度。(⚠️:在这个情景中,单个字符的最长公共前后缀的长度为0,不是1)

  • next数组
    next[i]表示为:在前 $ i $ 位 ( 0 ~ i − 1 ) (0~i-1) 0i1的字符串中的最长公共前后缀的长度。

    • 比如: aba 的字符串中的最长公共前后缀的长度为 1 1 1({a})
    • 比如:abcab的最长公共前后缀的长度为 2 2 2 {ab}

    KMP算法的核心是:当某个位置匹配失败时,移动到这个位置之前的字符串的最长前缀的下一个字符继续匹配。而寻找这个位置的任务就交给next数组来完成

  • 求next数组
    将问题转化为:求最长的公共前后缀问题
    这个问题我感觉像是一个dp问题

    1. next[0] = -1 // 默认初始化为-1,因为当第一位置就匹配失败时,两个坐标都要向前进行+1操作
    2. next[1] = 0 // 单个字符的最长公共前后缀的长度为0
    3. next[i]的情况: 表示的是前i位的字符串中最长公共前后缀长度,也就是说
      1. next[i]中存储的数字是最长公共前缀的最后一个字符的下一个字符的坐标(在代码中一直用k来进行代替)
      2. sz[next[i]]等价于sz[k] 表示的是前i位的字符串中最长公共前缀串的下一个字符。而sz[i]恰恰表示的是该段子串的最后一个字符
        • sz[i]==sz[k] 也就是最长前缀的后一个字符与该段子串的最后一个字符相匹配时,就要将最长公共前后缀的长度去进行+1操作
        • 如果sz[i]!=sz[k] ,则该段子串的最长公共前后缀的长度仍然和之前长度相同,k不变。

// 求特定模式串(子串)的next数组
vector<int> re_next(string zs){
    
    
    int len = zs.length();
    vector<int> next(len,0);
    int k = -1;
    int i = 0;
    while(i<len){
    
    
        if(k==-1||zs[k]==zs[i]){
    
    
            next[i] = k;
            i++;k++;
        }else{
    
    
            next[i++] = k;
        }
    }
    return next;
}

求index坐标

求index的坐标基本就不难理解了

//求坐标
int re_index(string ss,string zs,vector<int> next){
    
    
    int len_s = ss.length();
    int len_z = zs.length();
    int i,j;
    i = 0;
    j = 0;
    while(j<len_z&&i<len_s){
    
    
        if(j==-1||ss[i]==zs[j]){
    
    
            i++;
            j++;
        }else{
    
    
            j = next[j];
        }
    }
    if(j==len_z){
    
    
        return i - j;
    }
    else{
    
    
        return -1;
    }
}

可以借助于视频来进行理解一下:

完整代码

/*---------------------------------------------------------------
              Proverb  : Make a little progress every day         
              AUthor   : programmerxing                               
              Question : 1                              
              Data     : 2023 - 01 - 29
              Time     : 23:21:22
----------------------------------------------------------------*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
// KMP算法
vector<int> re_next(string zs){
    
    
    int len = zs.length();
    vector<int> next(len,0);
    int k = -1;
    int i = 0;
    while(i<len){
    
    
        if(k==-1||zs[k]==zs[i]){
    
    
            next[i] = k;
            i++;k++;
        }else{
    
    
            next[i++] = k;
        }
    }
    return next;
}
int re_index(string ss,string zs,vector<int> next){
    
    
    int len_s = ss.length();
    int len_z = zs.length();
    int i,j;
    i = 0;
    j = 0;
    while(j<len_z&&i<len_s){
    
    
        if(j==-1||ss[i]==zs[j]){
    
    
            i++;
            j++;
        }else{
    
    
            j = next[j];
        }
    }
    if(j==len_z){
    
    
        return i - j;
    }
    else{
    
    
        return -1;
    }
}
int main()
{
    
    
    string zs,ss;
    cin>>ss>>zs;
    vector<int> next = re_next(zs);
    for(int e : next){
    
    
        cout<<e<<' ';
    }
    cout<<endl;
    cout<<re_index(ss,zs,next)<<endl;
    return 0;                   
}

猜你喜欢

转载自blog.csdn.net/qq_45575167/article/details/128842295