简介

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. salesforce零基础学习(一百)Mobile Device Tracking

    本篇参考: Mobile Device Tracking (salesforce.com) UserDevice | SOAP API Developer Guide | Salesforce Dev ...

  2. Http中的options请求

    引自:https://www.jianshu.com/p/5cf82f092201.https://www.cnblogs.com/mamimi/p/10602722.html 一.options是什 ...

  3. JavaScript中eval的替代方法

    引自:https://www.cnblogs.com/lxg0/p/7805266.html 通常我们在使用ajax获取到后台返回的json数据时,需要使用 eval 这个方法将json字符串转换成对 ...

  4. 深圳某小公司面试题:AQS是什么?公平锁和非公平锁?ReentrantLock?

    AQS总体来说没有想象中那么难,只要了解它的实现框架,那理解起来就不是什么问题了. AQS在Java还是占很重要的地位的,面试也是经常会问. 目前已经连载11篇啦!进度是一周更新两篇,欢迎持续关注 [ ...

  5. 用xmind设计用例:

    注意一个原则:清晰明了,简单高效 注意不要写成需求分析,从测试的角度对场景进行分类管理 注意点: 1.思维导图重要的是逻辑清晰归类,注意有不要太多具体的操作步骤 举个例子(来源:https://www ...

  6. HTML Standard系列:Event loop、requestIdleCallback 和 requestAnimationFrame

    HTML Standard系列:Event loop.requestIdleCallback 和 requestAnimationFrame - 掘金 https://juejin.im/post/5 ...

  7. (Oracle)误删oracle表结构恢复

    在操作数据库时,我们常常会不小心把表结构删除了.有时候建表很麻烦大到100多个字段,而又找不到当初的建表语句.其实这时候不用担心,oracle和咱们widows一样,他也有个回收站,只要你没有清除回收 ...

  8. linux上jar项目启动脚本

    ---------------启动:start.sh #!/bin/bashcd `dirname $0`BIN_DIR=`pwd`cd ..DEPLOY_DIR=`pwd`CONF_DIR=$DEP ...

  9. Linux的.a、.so和.o文件 windows下obj,lib,dll,exe的关系 动态库内存管理 动态链接库搜索顺序 符号解析和绑定 strlen函数的汇编实现分析

    Linux的.a..so和.o文件 - chlele0105的专栏 - CSDN博客 https://blog.csdn.net/chlele0105/article/details/23691147 ...

  10. loj10005数列极差

    题目描述 佳佳的老师在黑板上写了一个由  n个正整数组成的数列,要求佳佳进行如下操作:每次擦去其中的两个数 a 和 b,然后在数列中加入一个数 a*b+1,如此下去直至黑板上剩下一个数为止,在所有按这 ...