KMP算法

简介: 文章目录前言一、KMP算法适用场景二、例题,模板1.模板2.AcWing 831. KMP字符串本题分析AC代码三、时间复杂度

文章目录

前言

一、KMP算法适用场景

二、例题,模板

1.模板

2.AcWing 831. KMP字符串

本题分析

AC代码

三、时间复杂度


前言

KMP算法新地址:数据结构:KMP(考研 + 竞赛),本文理解较浅,思路不清晰,可前往新地址去查看,为最新的理解。


一、KMP算法适用场景

KMP算法就是能快速找到一个模板串是否有子串的算法,这里我们直接用题目去分析,就本人目前而言,KMP算法感觉是最难理解的算法,如果不能理解,建议背下来模板先用,后续再慢慢理解


二、例题,模板

1.模板

本模板来自AcWing算法基础课

// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
//求模式串的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;
}
// 匹配
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];
        // 匹配成功后的逻辑
    }
}

2.AcWing 831. KMP字符串

本题链接:AcWing 831. KMP字符串

本博客给出本题截图:

image.png

本题分析

根据例子去讲解本算法,首先s数组存储的是模板串,p数组存储的是子串


接下来举一个栗子:

模板串(s):{aabaabaaf}

子 串 (p) :{aabaaf}

关于ne数组:ne[i]存储的是模式串中每个前缀最长的能匹配前缀子串的结尾字符的下标,同样也表示这一前缀和后缀最长相等的长度

KMP算法的实现:

s:aabaabaaf

p:aabaaf

012345678

我们通过观察可以知道在第5位的时候模板串和子串不匹配

按照最暴力的做法接下来我们应该把子串向后移动一个位置,即拿p[0]和s[1]进行比较,如果不能匹配,那么拿p[0]和s[2]比较,依次类推,这显然是很麻烦的

如果我们能预处理出来这么一个东西,即找到p[5]之前的元素的最长公共子序列:即对于{aabaa},找到最长公共子序列{aa},那么我们就可以进行如下操作:

直接把数组p平移为

aabaabaaf

  aabaaf

就可以省去很多的步骤,从而达到优化代码的结果,接下来我们来看如何实现这种操作

计算数组p的每一位的ne

ne[0] = 0,显然对于单独一个字母,不存在子序列

ne[1] = 1,a和a匹配

ne[2] = 0,{a,a,b}中不存在前缀 = 后缀

ne[3] = 1,{a,a,b,a}中,a和a匹配

ne[4] = 2,{a,a,b,a,a}中,aa和aa披人皮

ne[5] = 0,{a,a,b,a,a,f}中不存在前缀 = 后缀


所以本例子中的ne数组就是{0,1,0,1,2,0}

然后我们把这个数组中的元素直接右移1变成{-1,0,1,0,1,2}(不需要理解为什么这么去做)

当我们p[5]和s[5]不匹配的时候,我们采取的操作是直接继续让s[5]去和p[2]进行比较即我们直接让s[5]去和p[ne[5]]去比较,假令遍历p数组的指针为j,那么这一步的操作就是让j = ne[j];,所以让我们子串与模板串不匹配的时候,执行这个操作就可以实现快速匹配


接下来说一下怎么求ne数组

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

初始化ne[1] = 0;

AC代码

#include <iostream>
using namespace std;
const int N = 100010, M = 1000010;
char s[M], p[N];
int ne[N];
int main()
{
    int n, m;
    cin >> n >> p + 1 >> m >> s + 1;
    for (int i = 2, j = 0; i <= n; i ++ )
    {
        while (j && p[i] != p[j + 1]) j = ne[j];    //注意j不能为0
        if (p[j + 1] == p[i]) j ++;              
        ne[i] = j;
    }
    for (int i = 1, j = 0; i <= m; i ++ )
    {
        while (j && p[j + 1] != s[i]) j = ne[j];    //不匹配的话就执行操作,注意j不能为0,即返回到头的话就没必要继续执行j = ne[j]了,否则会死循环
        if (p[j + 1] == s[i]) j ++;         //匹配的话就让j指针往后移
        if (j == n)                                 //证明成功找到了子串
        {
            cout << i - n << ' ';
            j = ne[j];                              //继续遍历,因为满足条件的起始下标不止可能一个
        }
    }
    return 0;
}


三、时间复杂度

关于KMP算法的时间复杂度以及证明,后续会给出详细的说明以及证明过程,目前先鸽了。

目录
相关文章
|
8月前
|
算法
数据结构中的KMP算法及其改进算法
KMP算法通过引入部分匹配表,有效避免了重复计算,从而将字符串匹配的时间复杂度降低到O(m+n)。通过进一步优化next数组,KMP算法的效率得到了进一步提升。对于大规模字符串匹配问题,KMP算法及其改进算法提供了高效的解决方案,是计算机科学领域的经典算法之一。
130 3
|
4月前
|
算法
第四章 KMP算法理论基础
第四章 KMP算法理论基础
40 0
|
4月前
|
算法
KMP算法
KMP算法
54 0
|
6月前
|
算法 C++
A : DS串应用–KMP算法
这篇文章提供了KMP算法的C++实现,包括计算模式串的next数组和在主串中查找模式串位置的函数,用于演示KMP算法的基本应用。
|
7月前
|
数据采集 算法 JavaScript
揭开JavaScript字符串搜索的秘密:indexOf、includes与KMP算法
JavaScript字符串搜索涵盖`indexOf`、`includes`及KMP算法。`indexOf`返回子字符串位置,`includes`检查是否包含子字符串。KMP是高效的搜索算法,尤其适合长模式匹配。示例展示了如何在数据采集(如网页爬虫)中使用这些方法,结合代理IP进行安全搜索。代码示例中,搜索百度新闻结果并检测是否含有特定字符串。学习这些技术能提升编程效率和性能。
170 1
揭开JavaScript字符串搜索的秘密:indexOf、includes与KMP算法
|
6月前
|
算法
KMP算法
KMP算法
48 0
|
7月前
|
算法 Java
KMP算法详解及其在字符串匹配中的应用
KMP算法详解及其在字符串匹配中的应用
|
8月前
|
人工智能 算法 BI
一篇文章讲明白KMP算法(俗称看毛片算法)
一篇文章讲明白KMP算法(俗称看毛片算法)
85 0
|
8月前
|
算法 Java
Java数据结构与算法:字符串匹配算法之KMP算法
Java数据结构与算法:字符串匹配算法之KMP算法
|
9月前
|
算法