简介

KMP算法由 Knuth-Morris-Pratt 三位科学家提出,可用于在一个 文本串 中寻找某 模式串 存在的位置。

本算法可以有效降低在一个 文本串 中寻找某 模式串 过程的时间复杂度。(如果采取朴素的想法则复杂度是 \(O(MN)\) )

这里朴素的想法指的是枚举 文本串 的起点,然后让 模式串 从第一位开始一个个地检查是否配对,如果不配对则继续枚举起点。

前置知识

真前缀

指字符串左部的任意子串(不包含自身),如 abcde 中的 a,ab,abc,abcd 都是真前缀但 abcde 不是。

真后缀

指字符串右部的任意子串(不包含自身),如 abcde 中的 e,de,cde,bcde 都是真后缀但 abcde 不是。

前缀函数

一个字符串中最长的、相等的真前缀与真后缀的长度, 如AABBAAA对应的前缀函数值是 \(2\) 。

原理

注意:在分析的时候,我们规定字符串的下标从 \(1\) 开始。

开始:

我们记扫描模式串的指针为j,而扫描文本串的指针为i,假设一开始i,j都在起点,然后让它们一直下去直到完全匹配或者失配,比如:

j
ABCD i
ABCDEFG

然后

 j
ABCD i
ABCDEFG

最后在此完成了一次匹配,类似地如果ABCD改为ABCC则在此失配。

   j
ABCD i
ABCDEFG

i,j运作模式如上。



KMP算法就是,当模式串和文本串失配的时候,j指针从真后缀的末尾跳到真前缀的末尾,然后从真前缀后一位开始继续匹配。(从而起到减少配对次数,这便是KMP算法的核心原理)

结合例子解释:

模式串: \(AABBAAA\)

文本串: \(AABBAABBAAA\)

j指针在最后一个A处失配。

      j
AABBAAA
i
AABBAABBAAA

因为此时 以j为尾的前缀 所对应的前缀函数值是 \(2\) ,所以 j指针 跳到这里:

 j
AABBAAA
i
AABBAABBAAA

然后从下一位开始继续配对:

  j
AABBAAA
i
AABBAABBAAA

最后

      j
AABBAAA
i
AABBAABBAAA

可以看出,KMP能够有效减少配对次数。

实现

我们记模式串p文本串s

从上面的模拟中,我们发现需要预处理出一个数组(记之为next[]),它储存模式串中前缀对应的前缀函数\(\pi()\),如对于字符串ABCABC

\(\pi(0)=0\) (因为什么都没有)

\(\pi(1)=0\) (A甚至没有真前缀真后缀

\(\pi(2)=0\) (AB

\(\pi(3)=0\) (ABC

\(\pi(4)=1\) (ABCA

\(\pi(5)=2\) (ABCAB

\(\pi(6)=3\) (ABCABC

同样地,我们发现如果用暴力朴素的想法来统计复杂度是 O(N^2) 不好,于是采用类似于上面的方法,只不过模式串配对的对象是自己罢了。

可以结合代码理解,并注意举例,尝试在纸上模拟这个过程。

for(int i=2,j=0;i<=lenp;i++){
while(j && p[j+1]!=p[i]) j=next_[j]; // 如果j指向元素的下一个元素会和当前配对位置失配,则j跳回去
if(p[j+1]==p[i]) j++; //如果能够配对上,j++
next_[i]=j; //记录当前位置的前缀函数π
}

完整代码:

#include<bits/stdc++.h>
using namespace std; const int N=1e6+5;
char p[N],s[N];
int next_[N]; int main(){
cin>>s+1>>p+1; int lenp=strlen(p+1),lens=strlen(s+1);
// build next array
for(int i=2,j=0;i<=lenp;i++){
while(j && p[j+1]!=p[i]) j=next_[j]; // 如果j指向元素的下一个元素会和当前配对位置失配,则j跳回去
if(p[j+1]==p[i]) j++; //如果能够配对上,j++
next_[i]=j; //记录当前位置的前缀函数π
} for(int i=1,j=0;i<=lens;i++){
while(j && p[j+1]!=s[i]) j=next_[j];
if(p[j+1]==s[i]) j++; // if match
if(j==lenp){
j=next_[j];
cout<<i-lenp+1<<endl;
}
} for(int i=1;i<=lenp;i++) cout<<next_[i]<<' ';
cout<<endl; return 0;
}

复杂度

\(O(N+M)\)

【算法】KMP算法的更多相关文章

  1. 数据结构与算法--KMP算法查找子字符串

    数据结构与算法--KMP算法查找子字符串 部分内容和图片来自这三篇文章: 这篇文章.这篇文章.还有这篇他们写得非常棒.结合他们的解释和自己的理解,完成了本文. 上一节介绍了暴力法查找子字符串,同时也发 ...

  2. 经典算法 KMP算法详解

    内容: 1.问题引入 2.暴力求解方法 3.优化方法 4.KMP算法 1.问题引入 原始问题: 对于一个字符串 str (长度为N)和另一个字符串 match (长度为M),如果 match 是 st ...

  3. 笔记-算法-KMP算法

    笔记-算法-KMP算法 1.      KMP算法 KMP算法是一种改进的字符串匹配算法,KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的.具体实现就是实现一 ...

  4. 值得花费一周研究的算法 -- KMP算法(indexOf)

    KMP算法是由三个科学家(kmp分别是他们名字的首字母)创造出来的一种字符串匹配算法. 所解决的问题: 求文本字符串text内寻找第一次出现字符串s的下标,若未出现返回-1. 例如 text : &q ...

  5. [C++] [算法] KMP算法

    KMP串匹配算法是一个经典的算法. 传统BF算法是传统的字符串匹配算法.很好理解.叶实现.但时间复杂度太高. 本文将从字符串模式字符串被称为.为了匹配字符串被称为主弦. KMP配时能够少移动从串的位置 ...

  6. 程序员必会算法-KMP算法

    KMP算法是一种优秀的字符串匹配算法,字符串匹配的常规算法是一步一步进行移位和比较操作,直至找到完全相匹配的字符串. 下面通过一个例子,为大家仔细说明KMP算法的使用和思路: 问题: 在字符串“DEA ...

  7. 算法 kmp算法

    kmp算法是改进后的字符匹配算法,它与bf算法的区别是,每次从串与主串匹配失败后,从串与主串匹配的位置不同. 下面具体说下这两种算法的区别: 主串:BABCDABABCDABCED 从串:ABCDAB ...

  8. BF算法 + KMP算法

    准备: 字符串比大小:比的就是字符串里每个字符的ASCII码的大小.(其实这样的比较没有多大的意义,我们关心的是字符串是否相等,即匹配等) 字符串的存储结构:同线性表(顺序存储+链式存储) 顺序存储结 ...

  9. 图解算法——KMP算法

    KMP算法 解决的是包,含问题. Str1中是否包含str2,如果包含,则返回子串开始位置.否则返回-1. 示例1: Str1:abcd123def Str2:123d 暴力法: 从str1的第一个字 ...

  10. 字符串匹配算法——KMP算法

    处理字符串的过程中,难免会遇到字符匹配的问题.常用的字符匹配方法 1. 朴素模式匹配算法(Brute-Force算法) 求子串位置的定位函数Index( S, T, pos). 模式匹配:子串的定位操 ...

随机推荐

  1. Linux下利用ifconfig命令查看和操纵网络接口

    为了说明这个问题,首先我们需要解释一下在Linux系统下"网络接口"的含义.通俗来讲,Linux中的所谓网络接口就是指本机的网卡,它相当于计算机的一台负责对网络进行收发数据的外设. ...

  2. 【CentOS7】Apache发布静态网页-超简单

    目前能够提供Web网络服务的程序有 IIS. Nginx和 Apache等.其中,IIS (Internet Information Services,互联网信息服务)是 Windows系统中默认的 ...

  3. pickle — Python object serialization

    pickle - Python object serialization  消息队列

  4. get_or_create update_or_create

    django/query.py at master · django/django https://github.com/django/django/blob/master/django/db/mod ...

  5. Flash 终将谢幕:微软将于年底( 2020 年 )停止对 Flash 的支持

    近日,微软宣布将于今年 12 月终止对 Adobe Flash Player 的支持,届时,微软旗下所有浏览器都将无法使用 Flash,Adobe 也不会在今年 12 月后发布安全更新.早在 2017 ...

  6. 九鼎S5PV210开发板的SD卡启动、uboot tftp升级内核镜像

    Note: 我的S5PV210板子的磁盘是emmc版本,不是nandflash版本. 1. SD卡内烧录uboot镜像,然后作为启动盘来启动emmc中的内核镜像和根文件系统. 只要破坏emmc内的ub ...

  7. Java 实现Redis客户端,服务端

    Java 实现Redis客户端,服务端 1.Java实现Redis发布订阅 1.1实例 2.[Redis]Java实现redis消息订阅/发布(PubSub) 3.java实现 redis的发布订阅 ...

  8. linux下安装 zookeeper-3.4.9并搭建集群环境

    本文主要记录作者在实践过程中实现在centos7环境下安装zookeeper并搭建集群的详细步骤,关于zookeeper本文将不做详细介绍,安装步骤详情如下: 前提准备:3台linux服务器(因为zo ...

  9. 20000套免费ppt模板获取攻略

    前言 又到年末了,发现需要用到简历,PPT这些的地方又多了.PPT这东西吧,颜值真的很重要,毕竟老板拉融资都是用ppt拉来的.只要ppt够精美,外加上你的故事讲得好,A轮指定不是问题呀.往小处说,就是 ...

  10. poj 1696 极角排序(解题报告)

    #include<iostream> #include<cmath> #include<algorithm> using namespace std; double ...