首页 > 编程语言 >【kmp算法】字符串匹配

【kmp算法】字符串匹配

时间:2023-12-15 16:12:32浏览次数:31  
标签:匹配 int ne 回退 算法 maxn 数组 kmp 字符串

一,解决问题

  kmp算法解决的是字符串匹配的问题,具体来说假定我们要在主串s[ ] 中匹配模式串p[ ],找到匹配到的位置loc;

二,具体实现和演变过程

  最自然的想法是暴力写法 (BF)枚举主串字符s[ i ] ,和模式串p[ j ]。一个一个匹配,如果匹配失败,i指针回退回起点,往前进一位,再次进行比较,知道匹配成功。

代码如下:

#include<bits/stdc++.h>
#define int long long
const int INF = 1e18 + 10,maxn = 1e5 + 10;
using namespace std;

char p[maxn],s[maxn];
int n,m;
signed main (){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    
    cin>>m>>(p + 1)>>n>>(s + 1);
    for(int i = 1; i <= n; i++){
        int j = 1;
        while(s[i] == p[j] && j <= m){
            i++;
            j++;
        }
        if(j == m + 1) {
            cout<< i - j + 1 << '\n';
        }
        i = i - j + 1;//回退
    }
    
    return 0;
}

  时间复杂度是O(nm) 因为对于s[i],最多被比较m次,每次比较都会++指针,共有n个字符会被比较。

  kmp算法就是通过一些方法减少了比较次数,减少了时间复杂度的做法。

具体来说,我们先想办法使得i指针不回退,首先,我们已经知道匹配失败的位置前面所有位置都是匹配成功的,假设匹配失败的前一个位置为j 那么匹配失败的位置为j + 1,因为j + 1匹配失败,那么我们需要向右滑动字符串p,此时假设有一个ne【】数组,记录了p[i] 使得 p[1,ne[j]] = p[j-ne[j]+1,j] 的值即最长相等真字串长度,那么由于p[1,j] 之前是匹配成功的,那么只需要将j 回退到p[1,ne[j]] = p[j-ne[j]+1,j] 即可,任然只需要比较p[j + 1] = s[i]。代码如下:

 

    for(int i = 1,j = 0; i <= m; i++){
        while(j && p[j + 1] != s[i]) j = ne[j];
        if(p[j + 1] == s[i]) j++;
        if(j == n){
            cout<<i - n<<' ';
            j = ne[j];
        }
    }

  那么问题来了,如何确定ne【】数组,我们采取相同的策略,假定现在匹配前缀p[1,j] 和后缀 p[i- j + 1,i]如果匹配成功,只需将长度j++,(还记得ne【】数组的定义吗)如果匹配失败,只需要前后缀各自减少长度ne[j] 同理,现在匹配失败的位置是p[j + 1],那么只需回退j指针就能找到前一个p[1,j - ne[j]] 和 p[i-ne[j]+1,i] (更新位置i时。i前面的ne数组已经确定)那么代码如下(j 表示的意义不仅是匹配位置,也有长度的意思,以为前缀表示为p[1,j]):

    //ne数组的生成过程中,考察ne[i]和ne[i-1] i >= 2;
    //完成ne[i-1]匹配后可以确定p[1,j] = p[i-j,i-1]
    //那么如果p[i] != p[j+1]
    //应该回退j舍弃部分p[1,j] 的后缀和 p[i-j,i-1] 的前缀
    //回退到j = ne[j] (i-1前的ne数组都已完成维护)
    //此时p[1,ne[j]] 是p[1,j] 的真子集并且p[1,ne[j]] = p[j-ne[j]+1,j]
    //匹配过程
    for(int i = 2, j = 0; i <= n; i++){
        while(j && p[i] != p[j + 1]) j = ne[j];
        if(p[i] == p[j + 1]) j++;
        ne[i] = j;
    }
    

  那么完整的代码如下:

#include<bits/stdc++.h>
#define int long long
const int INF = 1e18 + 10,maxn = 1e6 + 10;
using namespace std;

char p[maxn],s[maxn];
int ne[maxn];
signed main (){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    
    int n,m;
    cin>>n>>(p+1)>>m>>(s+1);
    //ne数组的生成过程中,考察ne[i]和ne[i-1] i >= 2;
    //完成ne[i-1]匹配后可以确定p[1,j] = p[i-j,i-1]
    //那么如果p[i] != p[j+1]
    //应该回退j舍弃部分p[1,j] 的后缀和 p[i-j,i-1] 的前缀
    //回退到j = ne[j] (i-1前的ne数组都已完成维护)
    //此时p[1,ne[j]] 是p[1,j] 的真子集并且p[1,ne[j]] = p[j-ne[j]+1,j]
    //匹配过程
    for(int i = 2, j = 0; i <= n; i++){
        while(j && p[i] != p[j + 1]) j = ne[j];
        if(p[i] == p[j + 1]) j++;
        ne[i] = j;
    }
    
    for(int i = 1,j = 0; i <= m; i++){
        while(j && p[j + 1] != s[i]) j = ne[j];
        if(p[j + 1] == s[i]) j++;
        if(j == n){
            cout<<i - n<<' ';
            j = ne[j];
        }
    }
    return 0;
}

 

标签:匹配,int,ne,回退,算法,maxn,数组,kmp,字符串
From: https://www.cnblogs.com/jiangjlon/p/17879566.html

相关文章

  • C#根据类的Name字符串找到类
    C#中根据类的名称字符串创建类的实例这种⽤法很像是⼯⼚类,但是我们不需要⾃⼰实现字符串到类型的对应关系,也不需要创建的类有继承关系,代码如下://第⼀步:得到类的全名(命名空间+类名)stringadaptorName=namespace+classname;//第⼆部:根据全名得到类的类型TypeadaptorType......
  • Access数据库的中长字符串字段
    CREATETABLEoauth2_registered_client(idvarchar(36)NOTNULL,client_idvarchar(64)NOTNULL,client_id_issued_attimestampNOTNULL,client_secretvarchar(255)NULL,client_secret_expires_attimestampNULL,client_namevarchar......
  • shell补-特殊玩法-生成随机字符串
    shell补-特殊玩法-生成随机字符串方法1:md5sum方法2:tr+/dev/urandom方法3:内置变量RANDOM;#方法1[root@localhostser]#opensslrand-base64108/54arQpCmQ12Q==[root@localhostser]##方法2必备[root@localhostser]#date+%N|md5sum###给日期加密;可以写其......
  • Java-常见的排序算法有哪些
    Java-常见的排序算法有哪些比较排序算法:冒泡排序(BubbleSort):过程:从左到右依次比较相邻的元素,如果顺序不对就交换它们,一轮比较会将最大的元素冒泡到末尾。优势:简单易懂,对于小型数据集表现较好。劣势:时间复杂度为O(n^2),性能相对较差。插入排序(InsertionSort):过......
  • 神经网络算法原理简述
    神经网络算法是一种模拟人类神经系统运作的机器学习算法。它由多个神经元(或称为节点)组成,每个神经元都与其他神经元连接,并通过这些连接传递信息。神经网络通过学习大量数据,自动调整连接的权重,从而实现模式识别、分类、回归等任务。神经网络算法的原理可以分为以下几个步骤:输入层:神......
  • 高分辨率拼接案例分析【基础算法】
    一、案例来源本例项目来源于群里面网友提问“在流水线上采集到的图片,相互之间位移基本确定,需要进行进一步精细拼接”希望得到的结果。具体而言,这是一块大型服务器板子,会走点拍100张图【特定设备】,每张图有部分重合,算下来应该七百多宽度重合,图像大小为5000多。难点是重合的全是......
  • 算法战斗第三天C++2
    A.DominopilingYouaregivenarectangularboardofM × Nsquares.Alsoyouaregivenanunlimitednumberofstandarddominopiecesof2 × 1squares.Youareallowedtorotatethepieces.Youareaskedtoplaceasmanydominoesaspossibleonthe......
  • 算法学习笔记二一冒泡排序
    目录什么是冒泡排序算法原理代码示例什么是冒泡排序​对给定数组进行遍历,每次比较相邻两个元素大小,若大的数值在前面则交换两数位置(升序),每完成一趟遍历数组中最大的元素都会上升到数组的末尾,这也是冒泡一词的由来。算法原理(升序)列表每相邻的数,如果前面比后面大,则交换这两个数......
  • 基于小波神经网络的网络流量预测算法matlab仿真
    1.算法运行效果图预览   2.算法运行软件版本matlab2022A 3.算法理论概述       网络流量能直接反映网络性能的好坏,网络流量的建模与预测对于大规模网络的规划设计、网络资源管理以及用户行为的调节等方面都具有积极意义。本课题首先介绍了网络流量的特征......
  • 基于FPGA的图像形态学腐蚀算法实现,包括tb测试文件和MATLAB辅助验证
    1.算法运行效果图预览 将FPGA的仿真结果导入到MATLAB,结果如下所示:   2.算法运行软件版本vivado2019.2 matlab2022a 3.算法理论概述      基于FPGA的图像形态学腐蚀算法实现主要依赖于图像处理的基本原理和数学形态学的基础知识。在图像处理中,形态学操......